Tipo de retorno de función virtual C++


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

Author: templatetypedef, 2011-01-12

3 answers

En algunos casos, sí, es legal que una clase derivada anule una función virtual utilizando un tipo de retorno diferente, siempre y cuando el tipo de retorno sea covariante con el tipo de retorno 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 un Base *. En la implementación derivada, esta función virtual se sobrescribe usando un tipo de retorno de Derived *. Aunque el tipo de retorno 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 un Base* y la operación está bien definida.

Más generalmente, el tipo de retorno de una función nunca se considera parte de su firma. Puede anular una función miembro con cualquier tipo de retorno siempre que el tipo de retorno sea covariante.

 69
Author: templatetypedef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-05 19:49:01

Sí. Se permite que los tipos de devolución sean diferentes siempre y cuando lo sean covariante. El estándar C++ lo describe así (§10.3/5):

El tipo de retorno de una función primordial será idéntico para el tipo de retorno de la función reemplazada o covariante con las clases de funciones. Si una función D::f reemplaza a una función B::f, el tipo de retorno de las funciones son covariantes si satisfacen lo siguiente criterios:

  • ambos son punteros a clases o referencias a clases98)
  • 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 cv y el tipo de clase en el tipo de retorno de D::f tiene la misma calificación cv o menos cv-calificación que el tipo de clase en el tipo de retorno de B::f.

La nota 98 señala que "los punteros multinivel a clases o las referencias a punteros multinivel a clases no están permitidos."

En resumen, si D es un subtipo de B, entonces el tipo de retorno de la función en D debe ser un subtipo del tipo de retorno de la función en B. El ejemplo más común es cuando los tipos de retorno se basan en D y B, pero no lo hacen tiene que serlo. Consideremos esto, donde tenemos dos jerarquías de tipos separadas:

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 llamador de func está esperando un puntero Base. Cualquier puntero Base servirá. Por lo tanto, si D::func promete devolver siempre un puntero Derived, entonces siempre satisfará el contrato establecido por la clase ancestro porque cualquier puntero Derived puede convertirse implícitamente a un puntero Base. Por lo tanto, las personas que llaman siempre obtendrán lo que esperan.


Además De permitir algunos lenguajes permiten que los tipos de parámetros de la función de sobreescritura también varíen. Cuando hacen eso, por lo general necesitan ser contravariantes. Es decir, si B::f acepta un Derived*, entonces a D::f se le permitiría aceptar un Base*. A los descendientes se les permite ser más flojos en lo que aceptarán, y más estrictos en lo que devuelvan. C++ no permite contravarianza de tipo parámetro. Si cambia los tipos de parámetros, C++ lo considera un nueva función, para que comience a sobrecargar y ocultar. Para obtener más información sobre este tema, consulte Covarianza y contravarianza (informática) en Wikipedia.

 45
Author: Rob Kennedy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-12 06:21:09

Una implementación de clase derivada de la función virtual puede tener Tipo de Retorno Covariante.

 2
Author: Nikolai Fetissov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-21 00:31:36