Errore di risoluzione del sovraccarico durante lo streaming dell’object tramite conversione implicita in stringa

Disclaimer: so che la conversione implicita alla stringa dovrebbe essere evitata, e che l’approccio corretto sarebbe un op<< sovraccarico per Person .


Considera il seguente codice:

 #include  #include  #include  struct NameType { operator std::string() { return "wobble"; } }; struct Person { NameType name; }; int main() { std::cout << std::string("bobble"); std::cout << "wibble"; Person p; std::cout << p.name; } 

Fornisce quanto segue su GCC 4.3.4 :

 prog.cpp: In function 'int main()': prog.cpp:18: error: no match for 'operator<<' in 'std::cout << p.Person::name' /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:112: note: candidates are: std::basic_ostream& std::basic_ostream::operator<<(std::basic_ostream& (*)(std::basic_ostream&)) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:121: note: std::basic_ostream& std::basic_ostream::operator<<(std::basic_ios& (*)(std::basic_ios&)) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:131: note: std::basic_ostream& std::basic_ostream::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:169: note: std::basic_ostream& std::basic_ostream::operator<<(long int) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:173: note: std::basic_ostream& std::basic_ostream::operator<<(long unsigned int) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:177: note: std::basic_ostream& std::basic_ostream::operator<<(bool) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:97: note: std::basic_ostream& std::basic_ostream::operator<<(short int) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:184: note: std::basic_ostream& std::basic_ostream::operator<<(short unsigned int) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:111: note: std::basic_ostream& std::basic_ostream::operator<<(int) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:195: note: std::basic_ostream& std::basic_ostream::operator<<(unsigned int) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:204: note: std::basic_ostream& std::basic_ostream::operator<<(long long int) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:208: note: std::basic_ostream& std::basic_ostream::operator<<(long long unsigned int) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:213: note: std::basic_ostream& std::basic_ostream::operator<<(double) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:217: note: std::basic_ostream& std::basic_ostream::operator<<(float) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:225: note: std::basic_ostream& std::basic_ostream::operator<<(long double) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:229: note: std::basic_ostream& std::basic_ostream::operator<<(const void*) [with _CharT = char, _Traits = std::char_traits] /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:125: note: std::basic_ostream& std::basic_ostream::operator<<(std::basic_streambuf*) [with _CharT = char, _Traits = std::char_traits] 

Come mai il free op<<(ostream&, string const&) non fa parte del set di sovraccarico? Ciò è dovuto a una combinazione tra il sovraccarico desiderato e l’istanziazione di un modello e … ADL?

14.8.1 / 4 in C ++ 98

Le conversioni implicite (clausola 4) verranno eseguite su un argomento della funzione per convertirle nel tipo del parametro della funzione corrispondente se il tipo di parametro non contiene parametri del modello che partecipano alla deduzione degli argomenti del modello.

Qui ti piacerebbe un’istanza di

 template  basic_ostream& operator<<(basic_ostream&, const basic_string&); 

da dedurre senza fornire esplicitamente alcun argomento del template. Quindi tutti gli argomenti contengono un parametro template che partecipa alla deduzione degli argomenti del template e quindi nessuno di essi può ottenere il suo valore da una conversione implicita.

È perché è un modello.

Affinché ciò funzioni, è necessario prima istanziare il modello e successivamente utilizzare l’operatore di conversione. Questo è l’ordine sbagliato, quindi non funziona.


Non importa se hai già utilizzato un operatore specifico in precedenza nel programma o meno. Ogni utilizzo è considerato separatamente

I sovraccarichi considerati come candidati sono quelli in cui tutti i parametri del modello possono essere dedotti da std :: ostream, o quelli che sono membri di quella class.


Cosa succede se aggiungiamo un operatore non modello?

 #include  #include  #include  struct NameType { operator std::string() { return "wobble"; } }; struct Person { NameType name; }; void operator<<(std::ostream& os, const std::string& s) // ** added ** { std::operator<<(os, s); } int main() { std::cout << std::string("bobble"); std::cout << "wibble"; Person p; std::cout << p.name; } 

Ora funziona e produce

  bobblewibblewobble 

È perché la funzione di conversione definita dall’utente non è considerata in ADL. ADL significa che il set di overload contiene una o più funzioni di overload dal namespace in cui è definito l’ argomento . Qui il tipo di argomento operator<< è NameType ma l' operator << (std::ostream&, const NameType&) non è stato definito nello spazio dei nomi in cui è definito NameType . Da qui l'errore, poiché la ricerca del sovraccarico appropriato si ferma proprio lì. Questo è ciò che ADL è. ADL non va oltre per esaminare la definizione di NameType per determinare se definisce una funzione di conversione definita dall'utente o meno.

Otterrai lo stesso errore se fai quanto segue:

 NameType name; std::cout << name ; //error: user-defined conversion not considered. 

Devi lanciarlo :

 std::cout << (std::string)name << std::endl; //ok - use std::string() 

Inoltre, potresti avere più di una funzione di conversione definita dall'utente:

 std::cout << (int)name << std::endl; //ok - use int() instead 

Uscita su ideone :

 wobble 100 

La conversione in stringa viene invocata solo in alcuni casi:

a) richiesto esplicitamente (string) p.name

b) assegnazione a una stringa string a = p.name

c) …

Se il caso presente non si adatta, puoi forzare la chiamata a ostream<<(ostream&,string) in almeno due modi:

  1. http://ideone.com/SJe5W Fare in modo che NameType sia una stringa (per ereditarietà pubblica).

  2. vai al caso a) : richiedendo esplicitamente la conversione come visto nell'esempio con la conversione in (int) .

Preferisco davvero l'opzione 1 .

Questo perché le conversioni definite dall’utente non possono essere concatenate. Per spiegare con un esempio:

 struct A { void operator = (const int i); }; struct B { operator int (); } A a; B b; a = b; // error! because, compiler will not match "A::operator=" and "B::operator int" 

Ecco la domanda simile , ho chiesto a volte indietro.

Nel tuo caso, le tue prime conversioni definite dall’utente sono,

(1) NameType::operator std::string()

(2) operator <<(ostream&, const std::string&) che è un po 'come ostream::operator<<(std::string&) .

Quando scrivi, cout << p.name; Ora due tipi di oggetti si trovano faccia a faccia:

 ostream (LHS) <====> NameType (RHS) 

Ora, l' operator <<(ostream&, const string&) viene invocato solo se RHS è una string . Ma qui è NameType ; quindi non è invocato.

E, NameType::operator string () viene invocato solo se, LHS è una string . Ma qui è ostream ; quindi non è invocato.

Per rendere vera questa equazione; il bot dei metodi dell'operatore sopra dovrebbe essere invocato dal compilatore. Ma questo non è supportato da C ++. Perché non è supportato, è descritto nel link che ho postato sopra.