12.6.4. Las trampas de la conversión automática de tipos

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().

Actividades ocultas

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.