Dado que el compilador debe decidir cómo realizar una conversión de tipos, puede
meterse en problemas si el programador no diseña las conversiones
correctamente. Una situación obvia y simple sucede cuando una clase
X
que puede convertirse a sí misma en una clase
Y
con un operator Y()
. Si la clase
Y
tiene un constructor que toma un argumento simple de tipo
X
, esto representa la conversión de tipos idéntica. El
compilador ahora tiene dos formas de ir de X
a
Y
, así que se generará una error de ambigüedad:
//: C12:TypeConversionAmbiguity.cpp class Orange; // Class declaration class Apple { public: operator Orange() const; // Convert Apple to Orange }; class Orange { public: Orange(Apple); // Convert Apple to Orange }; void f(Orange) {} int main() { Apple a; //! f(a); // Error: ambiguous conversion } ///:~
Listado 12.24. C12/TypeConversionAmbiguity.cpp
La solución obvia a este problema es no hacerla. Simplemente proporcione una ruta única para la conversión automática de un tipo a otro.
Un problema más difícil de eliminar sucede cuando proporciona conversiones automáticas a más de un tipo. Esto se llama a veces acomodamiento (FIXME):
//: C12:TypeConversionFanout.cpp class Orange {}; class Pear {}; class Apple { public: operator Orange() const; operator Pear() const; }; // Overloaded eat(): void eat(Orange); void eat(Pear); int main() { Apple c; //! eat(c); // Error: Apple -> Orange or Apple -> Pear ??? } ///:~
Listado 12.25. C12/TypeConversionFanout.cpp
La clase Apple
tiene conversiones automáticas a
Orange
y a Pear
. El elemento
capcioso aquí es que no hay problema hasta que alguien inocentemente crea dos
versiones sobrecargadas de eat()
. (Con una única versión el
codigo en main()
funciona correctamente).
De nuevo la solución -- y el lema general de la conversión automática de tipos --
es proporcionar solo una conversión automática de un tipo a otro. Puede tener
conversiones a otros tipos, sólo que no deberían ser
automáticas. Puede crear llamadas a funciones explícitas con
nombres como makeA()
y makeB()
.
La conversión automática de tipos puede producir mas actividad subyacente de la
que se podría esperar. Mire esta modificación de
CopyingVsInitialization.cpp
como un pequeño rompecabezas:
//: C12:CopyingVsInitialization2.cpp class Fi {}; class Fee { public: Fee(int) {} Fee(const Fi&) {} }; class Fo { int i; public: Fo(int x = 0) : i(x) {} operator Fee() const { return Fee(i); } }; int main() { Fo fo; Fee fee = fo; } ///:~
Listado 12.26. C12/CopyingVsInitialization2.cpp
No hay un constructor para crear Fee fee
de un objeto
Fo
. Sin embargo, Fo
tiene una
conversión automática de tipos a Fee
. No hay un
constructor de copia para crear un Fee
a partir de un
Fee
, pero ésa es una de las funciones especiales que el
compilador puede crear. (El constructor por defecto, el constructor de
copia y operator=
) y el destructor pueden sintetizarse
automáticamente por el compilador. Así que, para la relativamente inocua
expresión:
Fee fee = fo;
se invoca el operador de conversión automático de tipo, y se crea un constructor de copia.
Use la conversión automática de tipos con precaución. Como con toda la sobrecarga de operadores, es excelente cuando reduce la tarea de codificación significativamente, pero no vale la pena usarla de forma gratuita.