c++ - Errores de "tipo de devolución covariante no válida" en clases anidadas con métodos que devuelven objetos basados en plantilla
class templates (3)
La función con la firma B::test(void)
devuelve un objeto de tipo Q<A>
, mientras que C::test(void)
(que es la misma firma, por lo que está sobrescribiendo la función) devuelve Q<B>
. Creo que eso es imposible.
Hasta donde yo sé, es imposible sobrecargar una función por tipo de retorno y las sobrescrituras de las funciones principales deben ajustarse al mismo tipo de retorno.
Del Estándar §10.3 / 7
El tipo de retorno de una función superior debe ser idéntico al tipo de retorno de la función anulada o covariante con las clases de las funciones. Si una función D :: f anula una función B :: f, los tipos de retorno de las funciones son covariantes si cumplen los siguientes criterios:
- ambos son punteros a las clases, ambos son referencias de valor a las clases, o ambos son referencias rvalue de las clases112.
- la clase en el tipo de devolución de B :: f es la misma clase que la clase en el tipo de devolución de D :: f, o es una clase base directa o indirecta no ambigua y accesible de la clase en el tipo de devolución de D :: F
- ambos punteros o referencias tienen la misma calificación de cv y el tipo de clase en el tipo de retorno de D :: f tiene la misma calificación cv o menos cv-qualification que el tipo de clase en el tipo de retorno de B :: f.
El siguiente código C ++ me da estos errores cuando se compila:
covariant.cpp:32:22: error: invalid covariant return type for ‘virtual Q<B> C::test()’
covariant.cpp:22:22: error: overriding ‘virtual Q<A> B::test()’
No quiero cambiar la línea virtual Q<B> test() {}
a virtual Q<A> test() {}
aunque elimina los errores de compilación. ¿Hay alguna otra manera de resolver este problema?
template <class T>
class Q
{
public:
Q() {}
virtual ~Q() {}
};
class A
{
public:
A() {}
virtual ~A() {}
};
class B
{
public:
B() {}
virtual ~B() {}
virtual Q<A> test() = 0;
};
class C : public B
{
public:
C() {}
virtual ~C() {}
virtual Q<B> test() {}
};
Usted no puede hacer eso. Las anulaciones de funciones virtuales no pueden cambiar el prototipo de la función, excepto casos muy específicos, como los tipos de retorno covariantes.
La devolución covariante sería válida si devolviera en la anulación virtual una subclase del tipo devuelto en la base virtual. Pero su Q<A>
y Q<B>
no están relacionados por herencia. El hecho de que B
sea una subclase de A
no hace ninguna diferencia aquí.
Q<B>
y Q<A>
son clases no relacionadas. Imagine que es un cliente de la test()
llamada B
test()
: ¿a qué le asigna el resultado, si no sabe qué tipo va a tener?
El hecho de que Q<A>
y Q<B>
sean instancias de la misma plantilla de clase no cambia el hecho de que son dos clases completamente independientes, posiblemente con un diseño completamente diferente (debido a la especialización de la plantilla).
Esto no sería diferente de hacer:
struct X
{
virtual std::string test() = 0;
};
struct Y : X
{
virtual int test() { return 42; } // ERROR! std::string and int are
// unrelated, just as Q<A> and Q<B>
};
La test()
llamada del cliente test()
en un puntero a X
esperaría que el resultado fuera una string
, pero "¡Vaya!", El objeto apuntado por ese puntero es de tipo Y
, y el tipo de devolución de Y::test()
es int
. ¿Qué debería pasar? ¿Un bloqueo en tiempo de ejecución?
Y y;
X* p = &y;
std::string s = p->test(); // D''OH!
C ++ es un lenguaje estáticamente tipado, lo que significa que la verificación de tipos se realiza en tiempo de compilación. En este caso, el mensaje del compilador está ahí para decirle que la clase derivada no se adhiere a la interfaz de la clase de la que proviene.
Si se está preguntando qué significa " tipo de retorno covariante no válido ", y en particular la palabra " covariante ", eso se explica fácilmente.
Supongamos que tiene una clase base B
con una función virtual foo()
que devuelve una X*
:
struct B
{
virtual X* foo();
};
Y supongamos que tiene una clase D
derivada de B
que anula a foo()
devolviendo un Y*
, donde Y
es una clase derivada de X
:
struct D : B
{
virtual Y* foo();
};
¿Es esto un problema? Bueno, la respuesta correcta viene de responder a esta pregunta un poco mejor: " ¿Sería eso un problema para un cliente que llama a foo()
que espera que se devuelva una X*
? "
Y la respuesta a esa pregunta es obviamente "No", ya que Y
es una clase derivada de X
, por lo que puede devolver un puntero a Y
lugar de un puntero a X
:
D d;
B* b = &d;
X* p = b->foo(); // Returns an Y*, but that''s OK, because a pointer to Y can be
// assigned to a pointer to X
Este es un ejemplo de un tipo de retorno covariante. En su ejemplo, el tipo de retorno de C::test()
no es covariante con respecto al tipo de retorno de B::test()
.