Tipo di ritorno della funzione virtuale C ++

È ansible che una class ereditata implementa una funzione virtuale con un tipo di ritorno diverso (non utilizzando un modello come reso)?

In alcuni casi, sì, per una class derivata è legale sovrascrivere una funzione virtuale utilizzando un tipo di reso diverso purché il tipo restituito sia covariante con il tipo di ritorno originale. Ad esempio, considera quanto segue:

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

Qui, Base definisce una funzione virtuale pura chiamata clone che restituisce una Base * . Nell’implementazione derivata, questa funzione virtuale viene sostituita utilizzando un tipo restituito di Derived * . Anche se il tipo di ritorno non è lo stesso della base, questo è perfettamente sicuro perché ogni volta che si scrive

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

La chiamata a clone() restituirà sempre un puntatore a un object Base , poiché anche se restituisce un Derived* , questo puntatore è implicitamente convertibile in una Base* e l’operazione è ben definita.

Più in generale, il tipo di ritorno di una funzione non è mai considerato parte della sua firma. È ansible sovrascrivere una funzione membro con qualsiasi tipo di restituzione purché il tipo restituito sia covariante.

Sì. I tipi di ritorno possono essere diversi a patto che siano covarianti . Lo standard C ++ lo descrive così (§10.3 / 5):

Il tipo di ritorno di una funzione di override deve essere identico al tipo di ritorno della funzione sostituita o covariante con le classi delle funzioni. Se una funzione D::f sovrascrive una funzione B::f , il tipo restituito delle funzioni è covariante se soddisfa i seguenti criteri:

  • entrambi sono puntatori a classi o riferimenti a classi 98)
  • la class nel tipo restituito di B::f è la stessa class della class nel tipo restituito di D::f oppure, è una class di base diretta o indiretta non ambigua della class nel tipo restituito di D::f e è accessibile in D
  • sia i puntatori che i riferimenti hanno la stessa qualifica di cv e il tipo di class nel tipo di ritorno di D::f ha la stessa qualifica di cv di o meno qualifica di cv rispetto al tipo di class nel tipo di ritorno di B::f .

La nota 98 ​​sottolinea che “non sono consentiti puntatori multi-livello a classi o riferimenti a puntatori multi-livello alle classi”.

In breve, se D è un sottotipo di B , allora il tipo di ritorno della funzione in D deve essere un sottotipo del tipo restituito della funzione in B L’esempio più comune è quando i tipi di ritorno sono essi stessi basati su D e B , ma non devono esserlo. Considera questo, dove abbiamo due gerarchie di tipi distinte:

 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; } 

Il motivo per cui questo funziona è perché ogni chiamante di func sta aspettando un puntatore Base . Qualunque puntatore Base farà. Quindi, se D::func promette di restituire sempre un puntatore Derived , soddisferà sempre il contratto stabilito dalla class antenato in quanto qualsiasi puntatore Derived può essere convertito implicitamente in un puntatore Base . Pertanto, i chiamanti otterranno sempre ciò che si aspettano.


Oltre a consentire la modifica del tipo di ritorno, alcune lingue consentono anche di variare i tipi di parametri della funzione di sostituzione. Quando lo fanno, di solito hanno bisogno di essere controversi . Cioè, se B::f accetta un Derived* , allora D::f potrebbe accettare una Base* . Ai discendenti è permesso di essere più liberi in ciò che accetteranno e più rigidi in ciò che ritorneranno. Il C ++ non consente la contravarianza del tipo di parametro. Se si cambiano i tipi di parametro, C ++ lo considera una funzione nuova di zecca, quindi si inizia a sovraccaricare e nascondersi. Per ulteriori informazioni su questo argomento, vedere Covarianza e controvarianza (informatica) in Wikipedia.

Un’implementazione di class derivata della funzione virtuale può avere un tipo di ritorno di tipo Covariant .