Conversión definida por el usuario
Permite una conversión implícita o una conversión explícita de un tipo clase a otro tipo.
[editar] Sintaxis
La función de conversión se declara como una función miembro no estática o plantilla de función miembro sin parámetros, un tipo de retorno no explícito y con el nombre en el formato:
operator id-de-tipo-de-la-conversión
|
(1) | ||||||||
explicit operator id-de-tipo-de-la-conversión
|
(2) | (desde C++11) | |||||||
explicit ( expresión ) operator id-de-tipo-de-la-conversión
|
(3) | (desde C++20) | |||||||
id-de-tipo-de-la-conversión es un id de tipo excepto que los operadores de función y array ()
o []
no se permiten en su declarador (por lo tanto, conversiones a tipos como puntero a array requieren un alias de tipo/typedef
o una plantilla de identidad: véase más abajo). Independientemente de typedef
, id-de-tipo-de-la-conversión no puede representar un array o un tipo de función.
Aunque no se permite el tipo de retorno en la declaración de una función de conversión definida por el usuario, la sec-decl-especificadores de la gramática de declaración puede estar presente y puede incluir cualquier especificador excepto el especificador-de-tipo o la palabra clave static
. Además de explicit
, también se permiten los especificadores inline
, virtual
, constexpr
(desde C++11), consteval
(desde C++20), y friend
(observa que friend
requiere un nombre calificado: friend A::operator B();
).
Cuando tal función miembro se declara en la clase X, realiza la conversión de X al id-de-tipo-de-la-conversión:
struct X { // conversión implícita operator int() const { return 7; } // conversión explícita explicit operator int*() const { return nullptr; } // ERROR: operador array no se permite en el id-de-tipo-de-la-conversión // operator int(*)[3]() const { return nullptr; } using arr_t = int[3]; operator arr_t*() const { return nullptr; } // de acuerdo si se hace mediante typedef // operator arr_t () const; // ERROR: no se permite conversión a array en ningún caso }; int main() { X x; int n = static_cast<int>(x); // de acuerdo: establece n a 7 int m = x; // de acuerdo: establece m a 7 int* p = static_cast<int*>(x); // de acuerdo: establece p a null // int* q = x; // ERROR: no es conversión implícita int (*pa)[3] = x; // de acuerdo }
[editar] Explicación
Una función de conversión definida por el usuario se invoca en la segunda etapa de la conversión implícita, que consiste en cero o un constructor de conversión, o cero o una conversión definida por el usuario.
Si tanto las funciones de conversión como los constructores de conversión pueden usarse para realizar alguna conversión definida por el usuario, tanto las funciones como los constructores de conversión se consideran por la resolución de sobrecarga en los contextos de inicialización de copia e inicialización por referencia, pero solamente se consideran los constructores en los contextos de inicialización directa.
struct To { To() = default; To(const struct From&) {} // constructor de conversión }; struct From { operator To() const {return To();} // función de conversión }; int main() { From f; To t1(f); // inicialización directa: llama al constructor // (observa que si el ctor de conversión no está disponible, el ctor de copia implícito // se seleccionará, y se llamará a la función de conversión para preparar su argumento) To t2 = f; // inicialización de copia: ambiguo // (observa que si la función de conversión es de un tipo no const, p. ej., // From::operator To();, en este caso se seleccionará en lugar del ctor) To t3 = static_cast<To>(f); // inicialización directa: llama al constructor const To& r = f; // inicialización por referencia: ambiguo }
Puede definirse una función de conversión a su propia (posiblemente calificada-cv) clase (o a una referencia a ella), a la base de su propia clase (o a una referencia a ella), y al tipo void, pero no puede ejecutarse como parte de la secuencia de conversión, excepto en algunos casos, a través del despacho virtual:
struct D; struct B { virtual operator D() = 0; }; struct D : B { operator D() override { return D(); } }; int main() { D obj; D obj2 = obj; // no llama a D::operator D() B& br = obj; D obj3 = br; // llama a D::operator D() a través del despacho virtual }
También puede llamarse usando la sintaxis de llamada a función miembro:
struct B {}; struct X : B { operator B&() { return *this; }; }; int main() { X x; B& b1 = x; // no llama a X::operatorB&() B& b2 = static_cast<B&>(x); // no llama a X::operatorB& B& b3 = x.operator B&(); // llama a X::operatorB& }
Cuando se hace una llamada explícita a la función de conversión, el id de tipo es voraz: es la secuencia más larga posible de símbolos que sea un id de tipo válido (incluyendo atributos, si es que los hay):
& x.operator int * a; // analizado como & (x.operator int*) a // no como & (x.operator int) * a
Puede usarse el marcador de posición auto en el id-de-tipo-de-la-conversión, indicando un tipo de retorno deducido: struct X { operator int(); // de acuerdo operator auto() -> short; // ERROR: el tipo de retorno al final no es parte // de la sintaxis operator auto() const { return 10; } // de acuerdo: tipo de retorno deducido }; Nota: No se permite que una plantilla de función de conversión tenga un tipo de retorno deducido. |
(desde C++14) |
Las funciones de conversión pueden heredarse y ser virtual, pero no pueden ser static. Una función de conversión en la clase derivada no oculta una función de conversión en la clase base a menos que conviertan al mismo tipo.
Una función de conversión puede ser una plantilla de función miembro, por ejemplo, std::auto_ptr<T>::operator auto_ptr<Y>. Véase plantillas miembro y deducción de argumentos de plantilla para reglas especiales que aplican.