2010-08-19 31 views
12

Espero que alguien pueda responder por qué lo siguiente no funciona. Oso con mí, sin embargo, sigo siendo en gran medida un novato ... No puedo llegar al fondo de por qué la siguienteOperador de conversión de sobrecarga C++ para tipo personalizado a std :: cadena

using namespace std; 
#include <string> 
#include <iostream> 

class testClass 
{ 
public: 
operator char*() {return (char*)"hi";}; 
operator int() {return 77;}; 
operator std::string () {return "hello";}; 
}; 

int main() 
{ 
char* c; 
int i; 
std::string s = "goodday"; 

testClass t; 

c = t; 
i = t; 
s = t; 

cout<< "char: " << c << " int: " << i << " string: "<<s<<endl; 

return 0; 
} 

me da un error de tiempo de compilación:

myMain.cpp: In function ‘int main()’: 
myMain.cpp:23: error: ambiguous overload for ‘operator=’ in ‘s = t’ 
/usr/include/c++/4.2.1/bits/basic_string.h:500: note: candidates are: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>] 
/usr/include/c++/4.2.1/bits/basic_string.h:508: note:     std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>] 
/usr/include/c++/4.2.1/bits/basic_string.h:519: note:     std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(_CharT) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>] 

Si yo no intente la asignación

s = t; 

funciona.

He estado intentando durante horas incluso comprender el mensaje de error, pero lo que más me desconcierta es que funciona para char *.

Agradezco cualquier pista. Gracias! Markus

Respuesta

1

En realidad, es porque std::string ofrece un operador de asignación que toma un const char*.

+1

Eso significa que operador std :: string =() y mis TestClass :: operador std :: string() están compitiendo por el trabajo? – Markus

+1

@Markus: como otros tuvieron la amabilidad de explicar, el operador de asignación está sobrecargado y puede manejar más de uno de los tipos para los que su clase tiene una conversión implícita. –

10

Lo que el error está tratando de explicar es que su asignación "s = t", donde s es una std::string, sería válida si t eran una std::string demasiado, o si eran una t [const] char*. Sus operadores de conversión puede convertir un t en cualquiera, por lo que el compilador no tiene ninguna base sobre la que elegir uno sobre el otro ....

Usted puede eliminar la ambigüedad de esta manera explícita mediante la selección de la conversión que desea:

s = t.operator std::string(); 
s = static_cast<std::string>(t); 

O puede proporcionar solo una de las conversiones y dejar que el usuario haga una conversión adicional cuando sea necesario.

Sin embargo, puede encontrar que cualquier operador de conversión es más problemático de lo que vale ... es revelador que std::string no proporciona un operador de conversión a const char*.

+0

Pero no se compila incluso si tomo la variable c y el operador char * fuera de la ecuación. Estoy tratando de evitar la desambiguación explícita por parte del usuario de la clase testClass en un intento de evitar la incapacidad de sobrecargar las funciones de los miembros que difieren en el valor de retorno. – Markus

+1

Según el mensaje de error, el tercer operador de asignación std :: string involucrado en la ambigüedad es del tipo "char" ... desafortunadamente su clase de prueba tiene una conversión implícita a int, y char es solo una (típicamente) de 8 bits int ... Simpatizo con su objetivo ... sería genial si existiera una buena solución - imagine no tener que llamar a std :: string :: c_str() tan a menudo - pero desafortunadamente los enfoques disponibles tienen problemas. Tal vez C++ debería admitir una notación para desambiguar tales situaciones, cuando el programador confía en que todas las coincidencias son funcionalmente equivalentes, pero por ahora ... :-(. –

+0

"Funcionalmente equivalente" no significa, sin embargo, equivalente. Construir/asignar una string con const std :: string & es probable que sea más eficiente que const char * (con GCC libstdC++, comparte el búfer y realiza copy-on-write, creo), incluso si de lo contrario parecen hacer lo mismo. –

3

No hay una regla exacta std :: string :: operator =. Los candidatos son, paráfrasis,

s = (const std::string)(std::string)t; 
s = (const char*)t; 
s = (char)(int)t; 

creo que las cosas van a funcionar si lo cambia a volver const std :: string. (EDITAR: Estoy equivocado.) También tenga en cuenta que la primera función debe devolver const char *. Si necesita convertir un literal de cadena a char *, está haciendo algo mal; los literales de cadena son no grabables.

+0

Acabo de probar eso y parece que no funciona. Y gracias por la pista. Soy consciente de que necesito pensar y aprender un poco más sobre cuando las cosas son o deberían ser ... – Markus

+0

No hay nada malo con el operador std :: string() ... construye un std :: string temporal del literal de la cadena eso es entonces "propiedad" de la persona que llama, por lo que cualquier operación no const en la misma no afecta la misma cadena de texto. Pero definitivamente debe ser operador const char *() para el primero. –

+0

Obviamente mi C++ - Fu no está a la altura. Debe elegir tanto a un elenco como a un operador de asignación, y no hay ninguna razón para que prefiera 'operator = (const std :: string &)' a 'operator = (const char *)'. No estoy seguro de si puede definir operadores de asignación global (es decir, ':: operator = (std :: string &, const testClass &)', pero si funciona, podría ser una solución.) –

9

$13.3.1.5/2 states- "The conversion functions of S and its base classes are considered. Those that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence (13.3.3.1.1) are candidate functions. Conversion functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type for this process of selecting candidate functions. Conversion functions that return “reference to cv2 X” return lvalues of type “cv2 X” and are therefore considered to yield X for this process of selecting candidate functions."

la asignación s = t funciona como sigue:

a) Todos los miembros en el tipo de 't' (TestClass) se consideran que pueden convertir 't' a 's'.

Candidate 1: operator string(); // s created using member string::operator=(string const&) 
Candidate 2: operator char *() // s created using member string::operator=(char const*) 
Candidate 3: operator char *() // s created using member string::operator=(char *) 

b) Todos los candidatos anteriores son viables (es decir, en ausencia de otros candidatos, el compilador puede resolver con éxito la llamada de función a cualquiera de ellos)

c) Sin embargo, ahora la mejor candidato viable tiene que ser determinado. Las secuencias de conversión involucrados son:

Candidate 1: U1 : operator string() 
Candidate 2: U2 : operator char*()->const qualification to match string::operator=(char const*) 
Candidate 3: U3 : operator char*() 

$13.3.3.1.1/3 states - "The rank of a conversion sequence is determined by considering the rank of each conversion in the sequence and the rank of any reference binding (13.3.3.1.4). If any of those has Conversion rank, the sequence has Conversion rank;"

Esto significa que U1, U2 y U3 son todos rango de conversión que tiene y en un primer nivel ni es mejor que el otro. Sin embargo, el estándar también establece

User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if they contain the same user-defined conversion function or constructor and if the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2.

Entonces, veamos qué significa esto.

Entre U1 y U2, que implican, diferentes funciones de conversión y por lo tanto ninguno es mejor que el otro

Entre U1 y U3, implican, diferentes funciones de conversión y por lo tanto ninguno es mejor que el otro

Entonces, ¿qué pasa con U1 y U2? Implican la misma función de conversión, que satisface la primera parte de la condición "y" por encima de

¿Qué pasa con la parte "y si la segunda secuencia de conversión estándar de U1 es mejor que la segunda secuencia de conversión estándar de U2?"

En U2, la segunda secuencia de conversión estándar requiere una calificación const, donde en U3 esto no es necesario. La segunda secuencia de conversión estándar de U3 es una coincidencia exacta.

Pero como lo indica la Tabla 9 en los estándares, la calificación CV también se considera una coincidencia exacta.

Por lo tanto, U2 y U3 también son realmente indistinguibles en lo que se refiere a la resolución de sobrecarga.

Esto significa U1, U2 y U3 son todos realmente tan buenos como los demás y el compilador encuentra la resolución de la llamada (como parte de la instrucción de asignación) como ambigua, ya que no hay ambigüedades mejor función viable

+0

amigos, tengo dificultades para formatear paso (c) en comillas de bloque. Necesitas ayuda. – Chubsdad

+0

Necesita una nueva línea adicional. – greyfade

+0

@greyfade: Gracias amigo – Chubsdad

0

Muy bien, muchas gracias a todos. Creo que estoy empezando a acostumbrarme, tipo de ...

Antes que nada, no tenía conocimiento del hecho de que ese carácter es solo un int de 8 bits. Gracias por esa aclaración.

por lo que entiendo que, debido a que hay tres operadores de asignación definidos para std :: string, cada uno con diferente argumento (cadena, char *, const char *) de la mano del lado derecho de la expresión

s=t 

no sabe, que tipo es tiene que convertir en una, ya que no son múltiples, potencialmente a juego (para esta asignación a std :: string) conversiones definidos, ya sea con

operator int() {return 77;}; 
operator std::string () {return "hello";}; 

(desde Char: int 8bit)

o

operator char*() {return (char*)"hi";}; 
operator std::string () {return "hello";}; 

¿Es eso cierto? Entonces, en términos idiotas, el lado izquierdo de la tarea no le está diciendo al lado derecho qué tipo espera, ¿entonces rhs tiene que elegir entre sus opciones, donde una es tan buena como alguna otra? std :: string operator = ¿es tolerante para mis intenciones?

Hasta ahora todo bien, pensé que lo conseguí, pero entonces, ¿por qué los siguientes crean ambigüedad también?

using namespace std; 
#include <string> 
#include <iostream> 

class testClass 
    { 
    public: 
    operator float() {return float(77.333);}; 
    operator std::string () {return "hello";}; 
    }; 

    int main() 
    { 
    std::string s = "goodday"; 
    testClass t; 

    s = t; 

    cout<< " string: "<<s <<endl; 

    return 0; 
    } 

Ahora solo hay un operador de conversión que coincida, ¿verdad? std :: string operator = no puede tomar flotantes, ¿o sí? ¿O es flotar de alguna manera equivalente a alguna variante de char otra vez?

entiendo el código como 's =' Contar la dcha: "dame una cadena, char * o const char *"

Rhs comprueba lo que puede proporcionar si se produjera una instancia de TestClass, y el único partido es testClass :: operator std :: string

Nuevamente, gracias por su paciencia, experiencia y tiempo chicos - Realmente lo aprecio.

+0

Eso se debe a que un 'flotante' puede convertirse a 'char' como parte de la Secuencia de Conversión Estándar integral flotante. Si tiene dificultades para comprender los mensajes del compilador, una forma de aprender el concepto es comentar "operator string()" y verificar qué sucede en el código. VS da "advertencia C4244: 'argumento': conversión de 'flotante' a 'char', posible pérdida de datos". Esto significa que el operador float() es un candidato y la coincidencia para llamar a este operador requiere la conversión de float a char – Chubsdad

+0

Ok, así que aún tengo mucho que aprender. ¡Gracias de nuevo a todos! Creo que mi, lo que yo pensaba como una solución elegante no funciona después de todo ... – Markus

Cuestiones relacionadas