Tipo de retorno de la función virtual C ++

¿Es posible que una clase heredada implemente una función virtual con un tipo de devolución diferente (sin usar una plantilla como retorno)?

En algunos casos, sí, es legal para una clase derivada anular una función virtual utilizando un tipo de devolución diferente, siempre que el tipo de devolución sea covariante con el tipo de devolución original. Por ejemplo, considere lo siguiente:

class Base { public: virtual ~Base() {} virtual Base* clone() const = 0; }; class Derived: public Base { public: virtual Derived* clone() const { return new Derived(*this); } }; 

Aquí, Base define una función virtual pura llamada clone que devuelve una Base * . En la implementación derivada, esta función virtual se reemplaza utilizando un tipo de devolución de Derived * . Aunque el tipo de devolución no es el mismo que en la base, esto es perfectamente seguro porque en cualquier momento escribirías

 Base* ptr = /* ... */ Base* clone = ptr->clone(); 

La llamada a clone() siempre devolverá un puntero a un objeto Base , ya que incluso si devuelve un Derived* , este puntero es implícitamente convertible a Base* y la operación está bien definida.

De manera más general, el tipo de devolución de una función nunca se considera parte de su firma. Puede anular una función de miembro con cualquier tipo de devolución siempre que el tipo de devolución sea covariante.

Sí. Los tipos de devolución pueden ser diferentes siempre que sean covariantes . El estándar de C ++ lo describe así (§10.3 / 5):

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 , el tipo de retorno de las funciones es covariante si cumple los siguientes criterios:

  • ambos son punteros a las clases o referencias a las clases 98)
  • la clase en el tipo de retorno de B::f es la misma clase que la clase en el tipo de retorno de D::f o, es una clase base directa o indirecta inequívoca de la clase en el tipo de retorno de D::f y es accesible en D
  • 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 .

La nota al pie 98 señala que “no se permiten punteros multinivel para clases o referencias a punteros multinivel para las clases”.

En resumen, si D es un subtipo de B , entonces el tipo de retorno de la función en D necesita ser un subtipo del tipo de retorno de la función en B El ejemplo más común es cuando los tipos de devolución se basan en D y B , pero no tienen que serlo. Considere esto, donde nosotros dos jerarquías de tipos separados:

 struct Base { /* ... */ }; struct Derived: public Base { /* ... */ }; struct B { virtual Base* func() { return new Base; } virtual ~B() { } }; struct D: public B { Derived* func() { return new Derived; } }; int main() { B* b = new D; Base* base = b->func(); delete base; delete b; } 

La razón por la que esto funciona es porque cualquier persona que llama de func espera un puntero Base . Cualquier puntero Base . Por lo tanto, si D::func promete devolver siempre un puntero Derived , siempre cumplirá el contrato establecido por la clase antecesora porque cualquier puntero Derived se puede convertir implícitamente en un puntero Base . Por lo tanto, las personas que llaman siempre obtendrán lo que esperan.


Además de permitir que el tipo de devolución varíe, algunos idiomas también permiten que los tipos de parámetros de la función de anulación varíen. Cuando hacen eso, generalmente necesitan ser contravariantes . Es decir, si B::f acepta un Derived* , entonces D::f podría aceptar una Base* . A los descendientes se les permite ser más flexibles en lo que aceptan, y más estrictos en lo que devuelven. C ++ no permite la contravariancia del tipo de parámetro. Si cambia los tipos de parámetros, C ++ considera que es una función completamente nueva, por lo que comienza a sobrecargarse y ocultarse. Para obtener más información sobre este tema, consulte Covarianza y contravarianza (ciencias de la computación) en Wikipedia.

Una implementación de clase derivada de la función virtual puede tener un tipo de retorno covariante .