web-dev-qa-db-fra.com

Remplacer une fonction membre avec un type de retour différent

Considérons l'exemple ci-dessous:

#include <iostream>

using namespace std;

class base
{
   public:
      virtual int func()
      {
         cout << "vfunc in base class\n";
         return 0;
      }
};

class derived: public base
{
   public:
      double func()
      {
         cout << "vfunc in derived class\n";
         return 0;
      }
};

int main()
{
   base *bptr = new derived;
   bptr->func();

   return 0;
}

Le compilateur donne une erreur pour le code ci-dessus qu'il existe un type en conflit pour la fonction remplacée. Pourquoi n'est-il pas possible de remplacer une fonction de la classe dérivée avec un type de retour différent?

Je crois que, afin de remplacer une fonction, la méthode virtuelle de la classe de base doit être redéfinie dans la classe dérivée. Pour redéfinir une méthode, les signatures des méthodes doivent être les mêmes. Comme le type de retour ne fait pas partie de la signature, je crois que même s'il existe une différence de type de retour, la méthode sera-t-elle toujours redéfinie? Dans ce cas pour le code ci-dessus, la fonction virtuelle func est redéfinie dans la classe dérivée avec un type de retour différent. Mais le compilateur renvoie une erreur. Ma compréhension est-elle correcte?

21
nitin_cherian

Remplacer signifie essentiellement que la méthode de la classe Base ou la méthode de la classe Derived sera appelée au moment de l'exécution en fonction de l'objet pointé par le pointeur.
Cela implique que:
i.e: chaque endroit où la méthode de la classe de base peut être appelée peut être remplacé par un appel à la méthode de la classe dérivée sans aucune modification du code appelant. 

Pour ce faire, le seul moyen possible est de limiter les types de retour des méthodes virtuelles qui le remplacent pour renvoyer le même type que la classe Base ou un type dérivé de celui-ci (types de retour co-variant) et la norme applique cette condition. 

Si la condition ci-dessus n'était pas en place, cela laisserait une fenêtre pour casser le code existant en ajoutant de nouvelles fonctionnalités.

23
Alok Save

Pour remplacer une fonction virtuelle, la valeur de retour doit être exactement la même *. C++ va pas convertir automatiquement entre double et int ici - après tout, comment saura-t-il quel type de retour vous voulez pour appeler à partir d'un pointeur de classe dérivée? Notez que si vous modifiez une partie de la signature (paramètres, const-ness, etc.), vous pouvez également modifier la valeur de retour.

* - à proprement parler, il doit être 'covariant'. Cela signifie que le type que vous renvoyez doit être un sous-ensemble du type de retour de la fonction parent. Par exemple, si la classe parent retourne un base *, vous pouvez retourner un derived *. Puisque deriveds sont simultanément aussi bases, le compilateur vous permet de remplacer de cette manière. Mais vous ne pouvez pas renvoyer de types totalement non liés tels que int et double; ce n’est pas parce qu’il ya une conversion implicite que le compilateur vous laissera faire ce genre de substitution.

10
bdonlan

Voir cette question . Pour résumer, vous ne pouvez remplacer une fonction virtuelle à l'aide d'un type de retour différent que si les types sont covariant .

6
ezod

Si vous souhaitez remplacer, vous devriez essayer d'utiliser un modèle. 

Voir ce qui suit:

#include <iostream>

using namespace std;

class base
{
   public:
      template<typename X> X func()
      {
         cout << "vfunc in base class\n";
         return static_cast<X>(0);
      }  
};    

class derived: public base
{
   public:
      template<typename X> X func()
      {
         cout << "vfunc in derived class\n";
         return static_cast<X>(2);
      }  
};    

int main()
{
   derived *bptr = new derived;
   cout << bptr->func<int>() << endl;
   cout << dynamic_cast<base*>(bptr)->func<int>() << endl;

   derived *bptr2 = new derived;
   cout << bptr->func<double>() << endl;
   cout << dynamic_cast<base*>(bptr)->func<int>() << endl;


   return 0;
}

Bien sûr, vous n'avez pas besoin de le déclarer sur deux classes différentes de cette façon, vous pourriez faire:

class base
{
   public:
      int func()
      {
         cout << "vfunc in base class\n";
         return 0;
      }  

      double func(){
        cout << "vfunc for double class\n";
        return 2.;

      }
};
2
Werner

La substitution n'est pas possible car les signatures sont différentes. Le but fondamental de la substitution est le polymorphisme, mais il n’est pas possible dans l’exemple ci-dessus.

0
kvk