C++ type de retour de la fonction virtuelle

est-il possible pour une classe héritée d'implémenter une fonction virtuelle avec un type de retour différent (n'utilisant pas un modèle comme retour)?

65
demandé sur templatetypedef 2011-01-12 06:35:54

3 réponses

dans certains cas, oui, il est légal pour une classe dérivée de surcharger une fonction virtuelle en utilisant un type de retour différent tant que le type de retour est covariant avec le type de retour original. Par exemple, considérons ce qui suit:

class Base {
public:
    virtual ~Base() {}

    virtual Base* clone() const = 0;
};

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

ici, Base définit une fonction virtuelle pure appelée clone qui renvoie une Base * . Dans l'implémentation dérivée, cette fonction virtuelle est écrasée en utilisant un retour type de Derived * . Bien que le type de retour n'est pas le même que dans la base, c'est parfaitement sûr parce que chaque fois que vous écririez

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

l'appel à clone() renvoie toujours un pointeur à un objet Base , car même s'il renvoie un Derived* , ce pointeur est implicitement convertible en un Base* et l'opération est bien définie.

plus généralement, le type de retour d'une fonction n'est jamais considéré comme faisant partie sa signature. Vous pouvez outrepasser une fonction de membre avec n'importe quel type de retour aussi longtemps que le type de retour est covariant.

69
répondu templatetypedef 2017-05-05 19:49:01

Oui. Les types de retour sont autorisés à être différents tant qu'ils sont covariant . La norme C++ La décrit ainsi (§10.3/5):

le type de retour d'une fonction prépondérante doit être identique au type de retour de la fonction prépondérante ou covariant avec les classes des fonctions. Si une fonction D::f l'emporte sur une fonction B::f , le type de retour des fonctions sont covariables si le satisfont les critères suivants:

  • tous 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 dans le type de retour de D::f ou, est une classe de base directe ou indirecte sans ambiguïté de la classe dans le type de retour de D::f et est accessible en D
  • les deux pointeurs ou références ont le même CV-qualification et le type de classe dans le type de retour de D::f a le même cv-qualification ou moins cv-qualification que le type de classe dans le type de retour de B::f .

note de bas de page 98 souligne que" les pointeurs à plusieurs niveaux vers des classes ou les références à des pointeurs à plusieurs niveaux vers des classes ne sont pas autorisés."

en bref, si D est un sous-type de B , puis le type de retour de la fonction dans D doit être un sous-type du type de retour de la fonction dans B . L'exemple le plus courant est lorsque les types de déclarations sont eux-mêmes basés sur D et B , mais ils ne doivent pas l'être. Considérez ceci, où nous séparons deux hiérarchies de type:

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 parce que n'importe quel appelant de func attend un Base pointeur. N'importe quel pointeur Base fera l'affaire. Ainsi, si D::func promet de toujours retourner un pointeur Derived , alors il satisfera toujours le contrat prévu par la classe ancêtre car tout pointeur Derived peut être implicitement converti en un pointeur Base . Ainsi, les appelants obtiennent toujours ce qu'ils attendent.


en plus de permettre au type de retour de varier, certaines langues permettent le les types de paramètres de la fonction de surpassement pour varier, aussi. Quand ils font cela, ils ont généralement besoin d'être contravariant . Autrement dit , si B::f accepte un Derived* , alors D::f serait autorisé à accepter un Base* . Les Descendants sont autorisés à être looser dans ce qu'ils accepteront, et plus strict dans ce qu'ils retourneront. C++ ne permet pas de contravariance de type de paramètre. Si vous modifiez l' types de paramètres, C++ considère qu'il s'agit d'une toute nouvelle fonction, donc vous commencez à vous surcharger et à vous cacher. Pour en savoir plus sur ce sujet, voir la Covariance et la contravariance (informatique) dans Wikipedia.

45
répondu Rob Kennedy 2011-01-12 06:21:09

Une classe dérivée de la mise en œuvre de la fonction virtuelle peut avoir un Covariant Type de Retour .

2
répondu Nikolai Fetissov 2015-10-21 00:31:36