Fallo de resolución de sobrecarga al transmitir objetos a través de la conversión implícita a cadena

Descargo de responsabilidad: que la conversión implícita a cadena debe evitarse, y que el enfoque adecuado sería una sobrecarga de OP para la Person .


Considera el siguiente código:

 #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; } 

Produce lo siguiente en 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] 

¿Cómo es que el free op<<(ostream&, string const&) no entra en el conjunto de sobrecarga? ¿Esto se debe a una combinación de la sobrecarga deseada que es una instanciación de plantilla y … ADL?

14.8.1 / 4 en C ++ 98

Las conversiones implícitas (cláusula 4) se realizarán en un argumento de función para convertirlo al tipo del parámetro de función correspondiente si el tipo de parámetro no contiene parámetros de plantilla que participan en la deducción del argumento de la plantilla.

Aquí le gustaría una instanciación de

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

se deduce sin proporcionar explícitamente ningún argumento de plantilla. Entonces, todos los argumentos contienen un parámetro de plantilla que participa en la deducción del argumento de la plantilla y, por lo tanto, ninguno de ellos puede obtener su valor de una conversión implícita.

Es porque es una plantilla.

Para que esto funcione, primero debe crear una instancia de la plantilla y luego utilizar el operador de conversión. Ese es el orden incorrecto, entonces no funciona.


No importa si ha usado un operador específico anteriormente en el progtwig o no. Cada uso se considera por separado

Las sobrecargas consideradas como candidatas son aquellas en las que todos los parámetros de la plantilla se pueden deducir de std :: ostream o aquellos que son miembros de esa clase.


¿Qué sucede si agregamos un operador que no es de plantilla?

 #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; } 

Ahora funciona y produce

  bobblewibblewobble 

Es porque la función de conversión definida por el usuario no se considera en ADL. ADL significa que el conjunto de sobrecarga contiene función (es) de sobrecarga del espacio de nombre en el que se define el argumento . Aquí el tipo de argumento para el operator< < es NameType pero el operator < < (std::ostream&, const NameType&) no se ha definido en el espacio de nombres en el que se define NameType . De ahí el error, ya que la búsqueda de una sobrecarga adecuada se detiene allí mismo. Eso es lo que es ADL. ADL no profundiza en la definición de NameType para determinar si define alguna función de conversión definida por el usuario o no.

Obtendrá el mismo error si hace lo siguiente:

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

Necesitas lanzarlo :

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

Además, es posible que tenga más de una función de conversión definida por el usuario:

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

Salida en ideone :

 wobble 100 

La conversión a cadena solo se invoca si en algunos casos:

a) solicitado explícitamente (string) p.name

b) asignación a una string a = p.name

c) …

Si el presente caso no se ajusta a ninguno, puede forzar la invocación a ostream< <(ostream&,string) de al menos dos formas:

  1. http://ideone.com/SJe5W Hacer que NameType sea una cadena (por herencia pública).

  2. ir al caso a) : solicitar explícitamente la conversión como se ve en el ejemplo con la conversión a (int) .

Realmente prefiero la opción 1 .

Esto se debe a que las conversiones definidas por el usuario no se pueden encadenar. Para explicar con un ejemplo:

 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" 

Aquí está la pregunta similar , le pregunté en algún momento atrás.

En su caso, sus primeras conversiones definidas por el usuario son,

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

(2) operator < <(ostream&, const std::string&) que es algo así como ostream::operator< <(std::string&) .

Cuando escribas, cout < < p.name; Ahora dos tipos de objetos se encuentran cara a cara:

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

Ahora, el operator < <(ostream&, const string&) se invoca solo si RHS es string . Pero aquí está NameType ; entonces no es invocado

Y, NameType::operator string () se invoca solo si, LHS es string . Pero aquí está ostream ; entonces no es invocado

Para hacer esta ecuación verdadera; bot el de los métodos de operador anteriores debe ser invocado por el comstackdor. Pero eso no es compatible con C ++. Por qué no es compatible, se describe en el enlace que publiqué arriba.