Oui. Les types de retour peuvent être différents tant qu'ils sont covariant . La norme C++ le décrit comme suit (§10.3/5) :
Le type de retour d'une fonction de surcharge est soit identique au type de retour de la fonction de surcharge, soit covariant avec les classes des fonctions. Si une fonction D::f
remplace une fonction B::f
Le type de retour des fonctions est covariant s'il satisfait aux critères suivants :
- les deux sont des pointeurs vers des classes ou des références à des classes 98)
- la classe dans le type de retour de
B::f
est la même classe que la classe du type de retour de D::f
ou, est une classe de base directe ou indirecte non ambiguë de la classe dans le type de retour de D::f
et est accessible en D
- les deux pointeurs ou références ont la même qualification cv et le type de classe dans le type de retour de
D::f
a la même qualification cv ou une qualification cv inférieure au type de classe dans le type de retour de B::f
.
La note de bas de page 98 précise que "les pointeurs de classes à plusieurs niveaux ou les références à des pointeurs de classes à plusieurs niveaux ne sont pas autorisés".
En bref, si D
est un sous-type de B
alors le type de retour de la fonction dans D
doit être un sous-type du type de retour de la fonction en B
. L'exemple le plus courant est celui où les types de retour sont eux-mêmes basés sur des D
y B
mais ils ne sont pas obligés de l'être. Prenons l'exemple suivant, où nous avons deux hiérarchies de types distinctes :
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 raison pour laquelle cela fonctionne est que tout appelant de func
attend un Base
pointeur. Tous les Base
fera l'affaire. Ainsi, si D::func
promet de toujours renvoyer un Derived
il satisfera toujours au contrat établi par la classe ancêtre, car tout pointeur Derived
peut être implicitement converti en un pointeur Base
pointeur. Ainsi, les appelants obtiendront toujours ce qu'ils attendent.
En plus de permettre la variation du type de retour, certains langages autorisent l'utilisation de la fonction types de paramètres de la fonction prioritaire. Lorsqu'ils le font, ils ont généralement besoin d'être contravariant . En d'autres termes, si B::f
accepte un Derived*
entonces D::f
serait autorisé à accepter un Base*
. Les descendants sont autorisés à plus lâche dans ce qu'ils acceptent, et plus strict dans ce qu'ils renvoient. Le C++ n'autorise pas la contravariance de type paramètre. Si vous changez les types de paramètres, le C++ considère qu'il s'agit d'une toute nouvelle fonction, ce qui entraîne des problèmes de surcharge et de dissimulation. Pour plus d'informations sur ce sujet, voir Covariance et contravariance (informatique) dans Wikipédia.