2011-09-29 16 views
5

Nel seguente codice di esempio, viene mostrato che boost :: tuple può essere creato implicitamente dal primo argomento del modello. Per questo motivo non sono in grado di scrivere un operatore << poiché diventa ambiguo.Come scrivere un operatore << << `per boost :: tuple?

Anche io non capisco perché ostringstream& << float è anche ambiguo. Questo non ha alcuna costruzione implicita. Perché questo dà anche un errore ambiguo?

#include <iostream> 
#include <boost/tuple/tuple.hpp> 
#include <sstream> 
#include <string> 

using namespace std; 

class Myclass 
{ 
}; 

typedef boost::tuple<int,float,Myclass> Mytuple; 

ostringstream& operator<<(ostringstream& os_, Mytuple tuple_) 
{ 
    float f = tuple_.get<1>(); 
    //os_ << (int)tuple_.get<0>(); // Error because int is implicitly converted into Mytuple. WHYY? 
    //os_ << tuple_.get<1>();  // No Clue Why this is ambiguous. 
    //os_ << tuple_.get<2>();  // Error because no matching operator. Fine. 
    return os_; 
} 

int main() 
{ 
    Mytuple t1; 
    t1 = 3;  // Working because int is implicitly converted into Mytuple!! WHY? 
    //t1 = 3.0f; // Error because no matching constructor. Fine. 
    return 0; 
} 

Errore Mesasge:

tupleTest2.C:18: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:

+0

'boost :: tuple' ha già un operatore' 'molto simile. Non vedo come ciò porti agli errori che stai ricevendo, ma potrebbe essere correlato. –

+0

Anche 'boost :: tuple' ha costruttori per 0..n degli elementi tuple, quindi il tuo ha un costruttore' Mytuple (int) 'che lo rende convertibile da' int'. –

+0

Compila per me, come dovrebbe, con gcc 4.5 e gcc 4.7 experimental. Quale versione del compilatore stai usando? – rodrigo

risposta

4

Il problema non è con il tupla, ma con il proprio operatore. Questo funziona bene:

ostream& operator<<(ostream& os_, Mytuple tuple_) 
{ 
    os_ << tuple_.get<0>(); // Error because int is implicitly converted into Mytuple. WHYY? 
    os_ << tuple_.get<1>();  // No Clue Why this is ambiguous. 
    //os_ << tuple_.get<2>();  // Error because no matching operator. Fine. 
    return os_; 
} 

Il problema è che l'ostringstream ereditano operator<< da ostream, che ha questa firma: ostringstream& operator<<(ostringstream& os_, Mytuple tuple_) è permesso. Poi il

ostream& operator<<(ostream& os, T t) 

(cambiamento T con tutti i tipi disponibili in C++, vedere operator<< reference page

EDIT

Ecco un esempio semplificato (senza tupla):

ostringstream& operator<<(ostringstream& os_, Mytuple tuple_) 
{ 
    const int i = tuple_.get<0>(); 
    os_ << i; // error in this line 
    return os_; 
} 

e l'errore è ora:

dfg.cpp: In function ‘std::ostringstream& operator<<(std::ostringstream&, Mytuple)’: 
dfg.cpp:18: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: 
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/ostream.tcc:111: note: candidate 1: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char, _Traits = std::char_traits<char>] 
dfg.cpp:14: note: candidate 2: std::ostringstream& operator<<(std::ostringstream&, Mytuple) 

Il messaggio di errore di cui sopra dice: non è possibile scegliere tra due operatori operator<<(ostream&,...) e operator<<(ostringstream&,...). This also raises another question : why on earth do you need operatore < < (ostringstream &, ...) `?

+0

Puoi spiegare qual è il problema? –

+0

Funziona. Ma perché funziona, ma "ostringstream" non funziona? In realtà voglio che funzioni con il mio stream personalizzato. – balki

+0

Non capisco. Sì, ostream ha l'operatore << per i tipi primitivi. Perché dovrebbe essere ambiguo con un operatore personalizzato che accetta una classe personalizzata? – balki

3

Quando si scrive

os << tuple_.get<0>(); 

Non esiste una funzione che corrisponde entrambi i parametri. Invece il compilatore ha una scelta per applicare una conversione implicita su entrambi parametro

std::ostream << int 

o

std::ostringstream << MyTuple 

Quest'ultimo sarebbe accadere con l'boost::tuple costruttore che può assumere qualsiasi numero di argomenti fino al numero di elementi di tuple . (E con float non riesce, perché float è convertibile in int.)

Quando sovraccarico operatori di flusso, utilizzare la classe di base come il lato sinistro (ostream o anche basic_ostream<CharT, Traits>


Edit:. Puoi disambiguare la chiamata lanciando il primo argomento .

ostringstream& operator<<(ostringstream& os_, Mytuple tuple_) 
{ 
    static_cast<std::ostream&>(os_) << tuple_.get<0>(); 
    static_cast<std::ostream&>(os_) << tuple_.get<1>();  
    static_cast<std::ostream&>(os_) << tuple_.get<2>();  // Error because no matching operator. Fine. 
    return os_; 
} 

Tuttavia, sovraccaricare l'operatore con ostringstream è ancora una cattiva idea, perché non funziona con operatore di concatenamento.

MyTuple a, b; 
ostringstream ss; 
ss << a << ' ' << b; 

invocherà:

1) ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)

2) ostream& ostream::operator<<(char)

3) ostream& operator<<(ostream&&, boost::tuple<int,float,Myclass>

+1

Sarebbe più facile disambiguare la scrittura: 'os.operator << (tuple_.get <1>());' – rodrigo

1

Tutte quelle persone che ti dice di usare ::std::ostream per il tipo, invece di ::std::ostringstream sono assoluti sono corretto Non dovresti usare ::std::ostringstream in questo modo.

Ma la mia carne principale con il tuo codice è l'angosciante mancanza di generalità. Funziona solo con un particolare tipo di tupla, e non tutti.

Quindi ho scritto uno operator << per ::std::tuple in C++ 0x che funziona per qualsiasi tupla i cui membri possono essere scritti singolarmente utilizzando operator <<. Probabilmente può essere tradotto relativamente facilmente per funzionare con il tipo di tupla di Boost. Eccolo:

template < ::std::size_t fnum, typename tup_type> 
void print_fields(::std::ostream &os, const tup_type &val) 
{ 
    if (fnum < ::std::tuple_size<tup_type>::value) { 
     ::std::cerr << "Fred " << fnum << '\n'; 
     os << ::std::get<fnum, tup_type>(val); 
     if (::std::tuple_size<tup_type>::value > (fnum + 1)) { 
     os << ", "; 
     } 
     print_fields<fnum + 1, tup_type>(os, val); 
    } 
} 

template < ::std::size_t fnum, typename... Elements> 
class field_printer; 

template <typename... Elements> 
class field_printer<0, Elements...> { 
public: 
    typedef ::std::tuple<Elements...> tup_type; 

    static void print_field(::std::ostream &os, const tup_type &val) { 
    } 
}; 

template < ::std::size_t fnum, typename... Elements> 
class field_printer { 
public: 
    typedef ::std::tuple<Elements...> tup_type; 

    static void print_field(::std::ostream &os, const tup_type &val) { 
     constexpr auto tupsize = ::std::tuple_size<tup_type>::value; 
     os << ::std::get<tupsize - fnum, Elements...>(val); 
     if (fnum > 1) { 
     os << ", "; 
     } 
     field_printer<fnum - 1, Elements...>::print_field(os, val); 
    } 
}; 

template <class... Types> 
::std::ostream &operator <<(::std::ostream &os, const ::std::tuple<Types...> &val) 
{ 
    typedef ::std::tuple<Types...> tup_type; 
    os << '('; 
    field_printer< ::std::tuple_size<tup_type>::value, Types...>::print_field(os, val); 
    return os << ')'; 
} 

Questo stampa tupla come "(element1, element2, ...elementx)".