Hay dos características que interactúan en juego aquí:
- Operadores de asignación no se heredan
- un constructor que no es explícita, o un operador de conversión (
operator T()
) definir un usuario de conversión que puede ser utilizado implícitamente como parte de una secuencia de conversión
Operadores ASIGNACIÓN Nunca se heredan
Un simple ejemplo de código:
struct Base {}; // implicitly declares operator=(Base const&);
struct Derived: Base {}; // implicitly declares operator=(Derived const&);
int main() {
Derived d;
Base b;
d = b; // fails
}
De ideone:
prog.cpp: In function ‘int main()’:
prog.cpp:7: error: no match for ‘operator=’ in ‘d = b’
prog.cpp:2: note: candidates are: Derived& Derived::operator=(const Derived&)
secuencia de conversión
Siempre que existe un desfase de "impedancia", tal como aquí:
Derived::operator=
espera un argumento Derived const&
Base&
se proporciona un
el compilador tratará de establecer una secuencia de conversión para cerrar la brecha. Tal secuencia de conversión puede contener al más una conversión definida por el usuario.
Aquí, se buscará:
- cualquier constructor de
Derived
que puede ser invocado con un Base&
(no explícito)
- un operador de conversión en
Base
que produciría un elemento Derived
No hay Base::operator Derived()
pero hay un constructor Derived::Derived(Base const&)
.
Por lo tanto nuestra secuencia conversión se define para nosotros:
Base&
Base const&
(trivial)
Derived
(usando Derived::Derived(Base const&)
)
Derived const&
(objeto temporal unido a una referencia const)
A nd entonces se llama Derived::operator(Derived const&)
.
En la acción
Si aumentamos el código con algunos rastros más, podemos see it in action.
#include <iostream>
struct Base {}; // implicitly declares Base& operator(Base const&);
struct Derived: Base {
Derived() {}
Derived(Base const&) { std::cout << "Derived::Derived(Base const&)\n"; }
Derived& operator=(Derived const&) {
std::cout << "Derived::operator=(Derived const&)\n";
return *this;
}
};
int main() {
Derived d;
Base b;
d = b;
}
que da salida:
Derived::Derived(Base const&)
Derived::operator=(Derived const&)
Nota: Prevención de esto?
Es posible, en C++, eliminar un constructor para ser utilizado en las secuencias de conversión. Para hacerlo, uno debe anteponer la declaración del constructor utilizando la palabra clave explicit
.
En C++ 0x, también es posible utilizar esta palabra clave en operadores de conversión (operator T()
).
Si aquí usamos explicit
antes de Derived::Derived(Base const&)
, el código se convierte en mal formado y debe ser rechazado por el compilador.
Quizás agregue un operador de asignación de copia y una declaración de registro. ;) – Xeo
La respuesta que eligió olvidó mencionar que esto sucede porque definió el constructor Derived (const Base &) y está realizando un downcast ... si no definió este construtor, obtendría un error de compilación al intentar realizar la tarea como se indica en mi respuesta. – Hazok
@Zach: el OP es el que elige, no se preocupe, se le han notificado todas las respuestas (y continuará recibiendo notificaciones). Si tu respuesta es excelente, los lectores cuidadosos lo subirán de todos modos y pronto se unirán a las respuestas principales y serán visibles para futuros lectores (menos cuidadosos) :) –