¿Cómo funcionan los operadores de conversión en C ++?

Considera este simple ejemplo:

template  class smartref { public: smartref() : data(new Type) { } operator Type&(){ return *data; } private: Type* data; }; class person { public: void think() { std::cout << "I am thinking"; } }; int main() { smartref p; p.think(); // why does not the compiler try substituting Type&? } 

¿Cómo funcionan los operadores de conversión en C ++? (es decir, ¿cuándo intenta el comstackdor sustituir el tipo definido después del operador de conversión?

Algunas situaciones aleatorias donde las funciones de conversión se usan y no se usan siguen.

En primer lugar, tenga en cuenta que las funciones de conversión nunca se utilizan para convertir al mismo tipo de clase o a un tipo de clase base.

Conversión durante el paso de la discusión

La conversión durante el paso del argumento usará las reglas para la inicialización de la copia. Estas reglas solo consideran cualquier función de conversión, sin tener en cuenta si se convierte a una referencia o no.

 struct B { }; struct A { operator B() { return B(); } }; void f(B); int main() { f(A()); } // called! 

La aprobación de argumentos es solo un contexto de inicialización de copia. Otra es la forma “pura” que utiliza la syntax de inicialización de copia

 B b = A(); // called! 

Conversión a referencia

En el operador condicional, la conversión a un tipo de referencia es posible, si el tipo convertido es un valor l.

 struct B { }; struct A { operator B&() { static B b; return b; } }; int main() { B b; 0 ? b : A(); } // called! 

Otra conversión a referencia es cuando vincula una referencia, directamente

 struct B { }; struct A { operator B&() { static B b; return b; } }; B &b = A(); // called! 

Conversión a indicadores de función

Puede tener una función de conversión a un puntero o referencia de función, y cuando se realiza una llamada, puede ser utilizada.

 typedef void (*fPtr)(int); void foo(int a); struct test { operator fPtr() { return foo; } }; int main() { test t; t(10); // called! } 

Esto puede llegar a ser bastante útil a veces.

Conversión a tipos no de clase

Las conversiones implícitas que ocurren siempre y en todas partes también pueden usar conversiones definidas por el usuario. Puede definir una función de conversión que devuelva un valor booleano

 struct test { operator bool() { return true; } }; int main() { test t; if(t) { ... } } 

(La conversión a bool en este caso se puede hacer más segura mediante el modismo safe-bool , para prohibir las conversiones a otros tipos de entero.) Las conversiones se desencadenan en cualquier lugar donde un operador incorporado espera un determinado tipo. Las conversiones pueden entrar en el camino, sin embargo.

 struct test { void operator[](unsigned int) { } operator char *() { static char c; return &c; } }; int main() { test t; t[0]; // ambiguous } // (t).operator[] (unsigned int) : member // operator[](T *, std::ptrdiff_t) : built-in 

La llamada puede ser ambigua, porque para el miembro, el segundo parámetro necesita una conversión, y para el operador integrado, el primero necesita una conversión definida por el usuario. Los otros dos parámetros coinciden perfectamente, respectivamente. La llamada puede ser no ambigua en algunos casos ( ptrdiff_t necesita ser diferente de int luego).

Plantilla de función de conversión

Las plantillas permiten algunas cosas agradables, pero es mejor tener mucho cuidado con ellas. Lo siguiente hace que un tipo sea convertible a cualquier tipo de puntero (los punteros de miembro no se ven como “tipos de puntero”).

 struct test { template operator T*() { return 0; } }; void *pv = test(); bool *pb = test(); 

Los “.” el operador no se puede descargar en C ++. Y cada vez que diga xy, no se realizará ninguna conversión automáticamente en x.

Las conversiones no son mágicas. El hecho de que A tenga una conversión a B y B tenga un método foo no significa que a.foo () llamará a B :: foo ().

El comstackdor intenta usar una conversión en cuatro situaciones

  1. Expulsa explícitamente una variable a otro tipo
  2. Usted pasa la variable como argumento a una función que espera un tipo diferente en esa posición (los operadores cuentan como funciones aquí)
  3. Asignas la variable a una variable de un tipo diferente
  4. Utiliza la variable copy-construct o inicializa una variable de un tipo diferente

Hay tres tipos de conversiones, distintas de las relacionadas con la herencia

  1. Conversiones incorporadas (por ejemplo, int-to-double)
  2. Construcción implícita, donde la clase B define un constructor tomando un solo argumento de tipo A, y no lo marca con la palabra clave “explícita”
  3. Operadores de conversión definidos por el usuario, donde la clase A define un operador B (como en su ejemplo)

Cómo el comstackdor decide qué tipo de conversión usar y cuándo (especialmente cuando hay múltiples opciones) es bastante complicado, y haría un mal trabajo tratando de condensarlo en una respuesta en SO. La sección 12.3 del estándar de C ++ analiza la construcción implícita y los operadores de conversión definidos por el usuario.

(Puede haber algunas situaciones de conversión o métodos en los que no he pensado, así que coméntelos o edítelos si ve que falta algo)

La conversión implícita (ya sea por operadores de conversión o constructores no explícitos) se produce al pasar parámetros a las funciones (incluidos los operadores sobrecargados y por defecto para las clases). Además de esto, hay algunas conversiones implícitas realizadas en tipos aritméticos (por lo que la adición de un char y un resultado largo en la adición de dos longs, con un resultado largo).

La conversión implícita no se aplica al objeto sobre el que se realiza una llamada a la función miembro: a los efectos de la conversión implícita, “esto” no es un parámetro de función.

Deberías hacer

 ((person)p).think(); 

El comstackdor no tiene la información para enviar automáticamente a la persona, por lo que necesita un casting explícito.

Si usaras algo como

 person pers = p; 

Luego, el comstackdor tiene información para transmitir implícitamente a la persona.

Puede tener “casting” a través de constructores:

 class A { public: A( int ); }; A a = 10; // Looks like a cast from int to A 

Estos son algunos ejemplos breves. Casting (implícito, explícito, etc.) necesita más explicaciones. Puede encontrar detalles en libros serios de C ++ (consulte las preguntas sobre libros en C ++ sobre desbordamiento de stack para obtener buenos títulos, como este ).

El comstackdor intentará un molde (!) Definido por el usuario (operador de conversión o ctor implícito) si intenta utilizar un objeto (referencia) de tipo T donde se requiere U

El . operador, sin embargo, siempre intentará acceder a un miembro del objeto (referencia) en su lado izquierdo. Así es como está definido. Si desea algo más elegante, eso es lo que el operator->() puede estar sobrecargado.

// Tabla virtual Fuction (VFT)

 #include  using namespace std; class smartref { public: virtual char think() { }//for Late bindig make virtual function if not make virtual function of char think() {} then become early binding and pointer call this class function smartref() : data(new char) { } operator char(){ return *data; } private: char* data; }; class person:public smartref { public: char think() { std::cout << "I am thinking"; } }; int main() { smartref *p;//make pointer of class person o1;//make object of class p=&o1;//store object address in pointer p->think(); // Late Binding in class person return 0; }