web-dev-qa-db-fra.com

Remplacement de la fonction surchargée d'une base en C ++

Duplicata possible:
résolution de surcharge C++

J'ai rencontré un problème où, après que ma classe a remplacé une fonction de sa classe de base, toutes les versions surchargées des fonctions étaient ensuite masquées. Est-ce par conception ou est-ce que je fais simplement quelque chose de mal?

Ex.

class foo
{
  public:
    foo(void);
    ~foo(void);
    virtual void a(int);
    virtual void a(double);
};

class bar : public foo 
{
  public:
    bar(void);
    ~bar(void);
    void a(int);
};

ce qui suit donnerait alors une erreur de compilation indiquant qu'il n'y a pas de a(double) dans bar.

main() 
{
  double i = 0.0;
  bar b;
  b.a(i);
}
46
Greg

Dans la barre de classe, ajoutez

using foo::a;

Il s'agit d'un 'gotcha' courant en C++. Une fois qu'une correspondance de nom est trouvée dans la portée d'une classe, elle ne recherche pas plus loin dans l'arborescence d'héritage les surcharges. En spécifiant la déclaration "using", vous amenez toutes les surcharges de "a" de "foo" dans la portée de "bar". Ensuite, la surcharge fonctionne correctement.

Gardez à l'esprit que s'il existe du code utilisant la classe "foo", sa signification pourrait être modifiée par les surcharges supplémentaires. Ou les surcharges supplémentaires pourraient introduire une ambiguïté et le code échouera à se compiler. Ceci est souligné dans la réponse de James Hopkin.

69
Fred Larson

C'est ainsi que fonctionnait la langue. Avant le mot clé sing, si vous surchargiez une fonction surchargée, vous deviez toutes les surcharger:

class bar : public foo 
{
  public:
    bar(void);
    ~bar(void);
    a(int);
    a(double d) { foo::a(d); }  // add this
}

Cela a assez ennuyé les gens pour que le comité de langue ait ajouté la fonction sing, mais certaines vieilles habitudes sont mortes; et les habitués † ont un bon argument.

Comme le souligne James Hopkins, en ajoutant sing, le programmeur exprime l'intention que la classe dérivée ajoutera, sans avertissement, toutes les futures surcharges de foo :: a () à sa liste de signatures acceptables.

Voici un exemple de ce qu'il décrit:

#include <iostream>
class Base {
public:
  virtual void f(double){ std::cout << "Base::Double!" << std::endl; }
  // virtual void f(int) { std::cout << "Base::Int!" << std::endl; } // (1)
  virtual ~Base() {}
};

class Derived : public Base {
public:
  // using Base::f; // (2)
  void f(double) { std::cout << "Derived::Double!" << std::endl; }
};

int main(int, char **) {
  Derived d;
  d.f(21);
  return 0;
}

La sortie sera "Derived :: Double!" car le compilateur promouvra l'argument entier en double. g ++ 4.0.1 -Wall n'avertira pas que cette promotion s'est produite.

Décommentez (1) pour simuler une modification future de Base en ajoutant la méthode Base :: f (int). Le code se compile, encore une fois sans avertissement même avec -Wall, et "Derived :: Double!" reste la sortie.

Décommentez maintenant (2) pour simuler une décision du programmeur dérivé d'inclure toutes les signatures Base :: f. Le code se compile (sans avertissements), mais la sortie est maintenant "Base :: Int!".

-

† Je ne peux pas penser à un mot anglais pour "ceux qui ont l'habitude" et "accro" est beaucoup trop fort.

21
Thomas L Holaday

C'est par conception. La résolution de surcharge est limitée à une seule étendue. Cela évite que certains cas désagréables de code valide ne changent de sens lorsque des fonctions supplémentaires sont ajoutées à une classe de base ou à la portée de l'espace de noms.

12
James Hopkin