¿Es posible que una clase heredada implemente una función virtual con un tipo de devolución diferente (que no utiliza una plantilla como resultado)?C++ virtual function return type
Respuesta
En algunos casos, sí, es legal que una clase derivada para anular una función virtual usando un retorno diferente tipo, siempre y cuando el tipo de retorno es covariante con el tipo de declaració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 un Base *
. En la implementación derivada, esta función virtual se anula utilizando un tipo de devolución de Derived *
. Aunque el tipo de retorno no es el mismo que en la base, esto es perfectamente seguro, porque cada vez que escribiría
Base* ptr = /* ... */
Base* clone = ptr->clone();
La llamada a clone()
siempre devolverá un puntero a un objeto Base
, ya que incluso si se devuelve una Derived*
, este puntero es implícitamente convertible a Base*
y la operación está bien definida.
Más en 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 covariant. El estándar de C++ describe así (§ 10.3/5):
El tipo de retorno de una función primordial será, o bien idéntica al tipo de retorno de la función reemplazada o covariante con las clases de las funciones. Si una función
D::f
anula una funciónB::f
, el tipo de retorno de las funciones son covariantes si el cumplir los siguientes criterios:
- ambos son punteros a clases o referencias a clases 98)
- la clase en el tipo de retorno de
B::f
es la misma clase que la clase en el tipo de retorno deD::f
o, es una clase base directa o indirecta inequívoca de la clase en el tipo de retorno deD::f
y es accesible enD
- ambos punteros o referencias tienen el la misma calificación de cv y el tipo de clase en el tipo de devolución de
D::f
tiene la misma calificación de cv o menos cv-qualification que el tipo de clase en el tipo de devolución deB::f
.
Nota al pie 98 señala que "punteros de varios niveles a clases o referencias a punteros de varios niveles a clases no están permitidos."
En resumen, si D
es un subtipo de B
, a continuación, el tipo de retorno de la función en D
es necesario que haya 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 dos jerarquías de tipos distintos:
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 está a la espera de func
un puntero Base
. Cualquier puntero Base
hará. 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 el tipo de retorno para variar, algunas lenguas permiten los tipos de parámetros de la función primordial de variar, también. Cuando lo hacen, generalmente deben ser contravariantes. Es decir, si B::f
acepta un Derived*
, entonces D::f
podría aceptar un Base*
. Los descendientes pueden tener más flojo en lo que aceptan, y más estricto 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 más información sobre este tema, vea Covariance and contravariance (computer science) en Wikipedia.
Bueno, aprendí algo hoy. +1. –
¿Es esta una característica real o un efecto secundario del tipo de devolución que no se utiliza en la resolución? –
@Martin, definitivamente una característica. Estoy bastante seguro de que la resolución de sobrecarga no tiene nada que ver con eso. El tipo de retorno * se * usa si está anulando una función. –
Una implementación de clase derivada de la función virtual puede tener un Covariant Return Type.
- 1. Función return type type
- 2. istream :: getline return type
- 3. typeOf on return type
- 4. java covariant return type
- 5. function wait with return until $ .getJSON finaliza
- 6. Can Lua's require function return multiple results?
- 7. ¿Por qué productIterator return type Iterator [Any]?
- 8. Cómo utilizar System.Action con return type?
- 9. C# Generics function
- 10. jQuery function execute on Button Click and Enter/Return (key)
- 11. C# Variable = new function() {};
- 12. ¿Qué significa '#type' en Haskell Foreign Function Interface?
- 13. Tipo de inferencia en el método return type
- 14. C/C++ __restrict type
- 15. C++ integer floor function
- 16. Cómo convertir [TYPE] a nulable <[TYPE]> en C#?
- 17. C++ plantilla determinar función return tipo
- 18. C# Linq return SortedList
- 19. C# string reference type?
- 20. Objective-C Type Inference
- 21. C++ Get Vector type
- 22. C++ virtual Const Función
- 23. C++ std :: set Find function overloading == operator
- 24. javascript function vs. (function() {...}());
- 25. C# Time a function using attribute
- 26. C++, base virtual abstracta protegida virtual virtual privado destructor
- 27. Virtual Default Destructors en C++
- 28. Problemas para comprender C++ `virtual`
- 29. C# Generics and Type Checking
- 30. C# equivalente de jQuery.parents (Type)
Este "Puede anular una función de miembro con cualquier tipo de devolución" no es correcto. Puede anular siempre que el tipo de devolución sea idéntico o covariante (lo que usted explicó), punto. No hay un caso más general aquí. – bronekk
@ bronekk- El resto de la oración que citó indica que el nuevo tipo de devolución debe ser utilizable en cualquier parte del tipo original; es decir, el nuevo tipo es covariante con el original. – templatetypedef
que el resto de la oración no es correcto; Imagine reemplazar 'Base *' con 'long' y' Derived * 'con' int' (o al revés, no importa). No funcionará – bronekk