Problemas con la herencia del operador = en C ++

Estoy teniendo problemas con la herencia de operator =. ¿Por qué este código no funciona y cuál es la mejor manera de solucionarlo?

#include  class A { public: A & operator=(const A & a) { x = ax; return *this; } bool operator==(const A & a) { return x == ax; } virtual int get() = 0; // Abstract protected: int x; }; class B : public A { public: B(int x) { this->x = x; } int get() { return x; } }; class C : public A { public: C(int x) { this->x = x; } int get() { return x; } }; int main() { B b(3); C c(7); printf("B: %d C: %d B==C: %d\n", b.get(), c.get(), b==c); b = c; // compile error // error: no match for 'operator= in 'b = c' // note: candidates are B& B::operator=(const B&) printf("B: %d C: %d B==C: %d\n", b.get(), c.get(), b==c); return 0; } 

Si no declara el operador de asignación de copia en una clase, el comstackdor declarará uno para usted implícitamente. El operador de asignación de copia implícitamente declarado ocultará cualquier operador de asignación heredado (lea sobre “ocultación de nombre” en C ++), lo que significa que cualquier operador de asignación heredado se volverá “invisible” al proceso de búsqueda de nombre no calificado (que es lo que sucede cuando se realiza b = c ), a menos que tome pasos específicos para “mostrarlos”.

En su caso, la clase B no tiene un operador de copiado explícitamente declarado. Lo que significa que el comstackdor declarará

 B& B::operator =(const B&) 

implícitamente. Ocultará el operador heredado de A La línea

 b = c; 

no comstack, porque, el único candidato aquí es el anterior B::operator = (el comstackdor ya te lo contó) implícitamente declarado; todos los demás candidatos están ocultos. Y dado que c no es convertible a B& , la asignación anterior no se comstack.

Si desea comstackr su código, puede usar using-declaration para mostrar el A::operator = heredado A::operator = agregando

 using A::operator =; 

a la definición de clase B El código ahora se comstackrá, aunque no será un buen estilo. Debe tener en cuenta que en este caso la asignación b = c invocará A::operator = , que asigna solo las porciones A de los objetos involucrados. (Pero aparentemente esa es tu intención).

Alternativamente, en casos como este siempre puede evitar el ocultamiento de nombres usando una versión calificada del nombre

 bA::operator =(c); 

Lo que está sucediendo es que el operator = predeterminado operator = que el comstackdor genera para cualquier clase que no tiene uno está ocultando la clase base ‘ operator = . En este caso particular, el comstackdor genera const B &B::operator =(const B &) para usted detrás de las escenas. Su tarea coincide con este operador e ignora por completo la que usted declaró en la class A Como una C& no se puede convertir en B& el comstackdor genera el error que ves.

Quieres que esto suceda, aunque ahora parece molesto. Impide que funcione el código como has escrito. No desea que código como este funcione porque permite tipos no relacionados (B y C tienen un ancestro común, pero las únicas relaciones importantes en herencia son las relaciones padre-> hijo-> nieto, no las relaciones entre hermanos) que se asignarán a uno otro.

Piénselo desde una perspectiva de ISA. ¿Se debe permitir que un Car sea ​​asignado a un Boat solo porque ambos son Vehicles ?

Para hacer algo como esto, debe usar el patrón Envelope / Letter . El sobre (también conocido como handle) es una clase especializada cuyo único trabajo es mantener una instancia de alguna clase derivada de una clase base particular (la letra). El controlador reenvía todas las operaciones, pero la asignación al objeto contenido. Para la asignación, simplemente reemplaza la instancia del objeto interno con una copia construida (usando un método ‘clon’ (aka constructor virtual)) copia del objeto asignado desde.

No se puede asignar a través de la jerarquía de esta manera: B y C son diferentes subclases de A. Puede asignar un B a un B o un C a un C pero no un C a un B o viceversa.

Probablemente quiera implementar operator= en B y C, delegando la parte A de la asignación a A::operator= antes de intentar esto. De lo contrario, las partes específicas de B y C de esas clases se perderán en la tarea.

Normalmente, operator = se define en B como

 B& operator=(B const &); 

Como B no es una base inequívoca y accesible de ‘C’, el comstackdor no permite la conversión de C a B.

Si realmente desea que se le asigne una ‘C’ a ‘B’, ‘B’ debe admitir un operador de asignación apropiado como

 B& operator=(C const &); 

(Probablemente no sea una solución y probablemente no sea lo que debería hacer) PERO … hay una forma en que puede forzar el problema si realmente debe:

  (A&)(*(&b)) = (A&)(*(&c))