Membres virtuels statiques C ++?


140

Est-il possible en C ++ d'avoir une fonction membre qui est à la fois staticet virtual? Apparemment, il n'y a pas de moyen simple de le faire (il static virtual member();s'agit d'une erreur de compilation), mais y a-t-il au moins un moyen d'obtenir le même effet?

C'EST À DIRE:

struct Object
{
     struct TypeInformation;

     static virtual const TypeInformation &GetTypeInformation() const;
};

struct SomeObject : public Object
{
     static virtual const TypeInformation &GetTypeInformation() const;
};

Il est logique d'utiliser à la GetTypeInformation()fois sur une instance ( object->GetTypeInformation()) et sur une classe ( SomeObject::GetTypeInformation()), ce qui peut être utile pour les comparaisons et vital pour les modèles.

Le seul moyen auquel je puisse penser consiste à écrire deux fonctions / une fonction et une constante, par classe, ou à utiliser des macros.

D'autres solutions?


12
Juste un commentaire secondaire: les méthodes statiques ne s'exécutent sur aucune instance, ce qui signifie qu'elles n'ont pas le pointeur implicite this. Cela étant dit, la constsignature in a method marque le thispointeur implicite comme constant et ne peut pas être appliqué aux méthodes statiques car elles n'ont pas le paramètre implicite.
David Rodríguez - dribeas

2
@cvb: Je reconsidérerais sérieusement le remplacement de votre exemple par du code qui n'implique pas de réflexion. Dans l'état actuel des choses, vous associez en quelque sorte deux problèmes distincts (bien que liés). Oui, et je sais que cela fait 5 ans et demi que vous l'avez posée.
einpoklum

L'une des fonctionnalités implicitement requises ici est que le compilateur vérifie que chaque objet d'une hiérarchie implémente une interface spécifique (où une ou plusieurs méthodes sont statiques). Fondamentalement, une vérification virtuelle pure de la méthode statique a beaucoup de sens, car si vous oubliez d'ajouter la méthode statique, le compilateur devrait se tromper. virtual n'est pas le mot-clé ici, c'est plus abstrait qui se trouve être une sorte de synonymes en C ++, sauf dans ce cas précis. Malheureusement, vous ne pouvez pas le faire avec C ++ actuellement.
xryl669

Réponses:


75

Non, il n'y a aucun moyen de le faire, puisque que se passerait-il lorsque vous appeliez Object::GetTypeInformation()? Il ne peut pas savoir quelle version de classe dérivée appeler car aucun objet ne lui est associé.

Vous devrez en faire une fonction virtuelle non statique pour fonctionner correctement; si vous souhaitez également pouvoir appeler la version d'une classe dérivée spécifique de manière non virtuelle sans instance d'objet, vous devrez également fournir une deuxième version statique non virtuelle redondante.


8
Si vous considérez la classe statique (ou les classes membres statiques) comme un singleton, tout devient évident - dans votre cas, il suffit d'appeler Object :: GetTypeInformation - de la même manière que d'appeler une méthode virtuelle régulière sur une instance de classe de base . (Bien sûr, si C ++ supportait les méthodes statiques virtuelles)
Spook

13
C'est un argument complètement spécieux. Si vous utilisez la classe au lieu d'un objet, il utiliserait naturellement la version de cette classe, au lieu de faire une distribution virtuelle. Rien de nouveau là-bas.
Deduplicator

54

Beaucoup disent que ce n'est pas possible, j'irais plus loin et je dirais que cela n'a pas de sens.

Un membre statique est quelque chose qui ne se rapporte à aucune instance, uniquement à la classe.

Un membre virtuel est quelque chose qui ne se rapporte directement à aucune classe, uniquement à une instance.

Ainsi, un membre virtuel statique serait quelque chose qui ne se rapporte à aucune instance ni à aucune classe.


42
C'est parfaitement significatif dans les langages où les classes sont des valeurs de première classe - par exemple Delphi a cela, et a aussi des méthodes "statiques virtuelles".
Pavel Minaev

4
Exactement. Une "fonction virtuelle" est (par définition) une fonction qui est liée dynamiquement , c'est-à-dire qu'elle est choisie à l'exécution en fonction du type dynamique d'un objet donné. Par conséquent, aucun objet = aucun appel virtuel.
Kos

7
Je pense aussi que les virtuels statiques sont significatifs. Il serait possible de définir des classes d'interface et d'inclure des méthodes statiques qui doivent être implémentées dans une classe dérivée.
bkausbk

34
Ce n'est pas si significatif pour une static virtualméthode, mais une méthode static pure virtual est très significative dans une interface.
Bret Kuhns

4
Il est parfaitement significatif d'avoir un fichier static const string MyClassSillyAdditionalName.
einpoklum

23

J'ai rencontré ce problème l'autre jour: j'avais des classes pleines de méthodes statiques mais je voulais utiliser l'héritage et les méthodes virtuelles et réduire la répétition du code. Ma solution était:

Au lieu d'utiliser des méthodes statiques, utilisez un singleton avec des méthodes virtuelles.

En d'autres termes, chaque classe doit contenir une méthode statique que vous appelez pour obtenir un pointeur vers une seule instance partagée de la classe. Vous pouvez rendre les vrais constructeurs privés ou protégés afin que le code extérieur ne puisse pas en abuser en créant des instances supplémentaires.

En pratique, l'utilisation d'un singleton ressemble beaucoup à l'utilisation de méthodes statiques, sauf que vous pouvez tirer parti de l'héritage et des méthodes virtuelles.


Cela va me coûter des performances - à moins que le compilateur ne puisse être certain que: 1. C'est en fait un singleton et 2. Rien n'en hérite, je ne pense pas qu'il puisse optimiser tous les frais généraux.
einpoklum

Si les performances de ce genre de choses vous inquiètent, alors C # n'est probablement pas le bon langage pour vous.
Nate CK

3
Ah, bon point. Évidemment, cela fait un moment que je n'y ai pas pensé depuis que je l'ai écrit en 2009. Permettez-moi de dire autrement: si ce genre de performance vous inquiète alors peut-être devriez-vous éviter complètement l'utilisation de l'héritage. L'affiche demandait spécifiquement des méthodes virtuelles, il est donc étrange que vous veniez ici pour vous plaindre de la surcharge des méthodes virtuelles.
Nate CK du

15

C'est possible!

Mais ce qui est exactement possible, limitons-nous. Les gens veulent souvent une sorte de "fonction virtuelle statique" à cause de la duplication du code nécessaire pour pouvoir appeler la même fonction via l'appel statique "SomeDerivedClass :: myfunction ()" et l'appel polymorphe "base_class_pointer-> myfunction ()". La méthode "légale" pour autoriser une telle fonctionnalité est la duplication des définitions de fonction:

class Object
{
public:
    static string getTypeInformationStatic() { return "base class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
}; 
class Foo: public Object
{
public:
    static string getTypeInformationStatic() { return "derived class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
};

Et si la classe de base a un grand nombre de fonctions statiques et que la classe dérivée doit les remplacer toutes et que l'on oublie de fournir une définition de duplication pour la fonction virtuelle. Bien, nous aurons une erreur étrange pendant l' exécution qui est difficile à localiser. Parce que la duplication de code est une mauvaise chose. Ce qui suit tente de résoudre ce problème (et je veux dire à l'avance qu'il est complètement sûr de type et ne contient aucune magie noire comme celle de typeid ou dynamic_cast :)

Donc, nous voulons fournir une seule définition de getTypeInformation () par classe dérivée et il est évident qu'il doit s'agir d'une définition de staticfonction car il n'est pas possible d'appeler "SomeDerivedClass :: getTypeInformation ()" si getTypeInformation () est virtuel. Comment pouvons-nous appeler une fonction statique d'une classe dérivée via un pointeur vers la classe de base? Ce n'est pas possible avec vtable car vtable stocke des pointeurs uniquement vers des fonctions virtuelles et comme nous avons décidé de ne pas utiliser de fonctions virtuelles, nous ne pouvons pas modifier vtable à notre avantage. Ensuite, pour pouvoir accéder à la fonction statique d'une classe dérivée via un pointeur vers la classe de base, nous devons stocker en quelque sorte le type d'un objet dans sa classe de base. Une approche consiste à créer un modèle de classe de base en utilisant un "modèle de modèle curieusement récurrent" mais ce n'est pas approprié ici et nous utiliserons une technique appelée "effacement de type":

class TypeKeeper
{
public:
    virtual string getTypeInformation() = 0;
};
template<class T>
class TypeKeeperImpl: public TypeKeeper
{
public:
    virtual string getTypeInformation() { return T::getTypeInformationStatic(); }
};

Maintenant, nous pouvons stocker le type d'un objet dans la classe de base "Object" avec une variable "keeper":

class Object
{
public:
    Object(){}
    boost::scoped_ptr<TypeKeeper> keeper;

    //not virtual
    string getTypeInformation() const 
    { return keeper? keeper->getTypeInformation(): string("base class"); }

};

Dans un gardien de classe dérivé doit être initialisé pendant la construction:

class Foo: public Object
{
public:
    Foo() { keeper.reset(new TypeKeeperImpl<Foo>()); }
    //note the name of the function
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

Ajoutons du sucre syntaxique:

template<class T>
void override_static_functions(T* t)
{ t->keeper.reset(new TypeKeeperImpl<T>()); }
#define OVERRIDE_STATIC_FUNCTIONS override_static_functions(this)

Maintenant, les déclarations des descendants ressemblent à:

class Foo: public Object
{
public:
    Foo() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

class Bar: public Foo
{
public:
    Bar() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "another class for the same reason"; }
};

usage:

Object* obj = new Foo();
cout << obj->getTypeInformation() << endl;  //calls Foo::getTypeInformationStatic()
obj = new Bar();
cout << obj->getTypeInformation() << endl;  //calls Bar::getTypeInformationStatic()
Foo* foo = new Bar();
cout << foo->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic()
Foo::getTypeInformation(); //compile-time error
Foo::getTypeInformationStatic(); //calls Foo::getTypeInformationStatic()
Bar::getTypeInformationStatic(); //calls Bar::getTypeInformationStatic()

Avantages:

  1. moins de duplication de code (mais il faut appeler OVERRIDE_STATIC_FUNCTIONS dans chaque constructeur)

Désavantages:

  1. OVERRIDE_STATIC_FUNCTIONS dans chaque constructeur
  2. surcharge de mémoire et de performances
  3. complexité accrue

Questions ouvertes:

1) il existe différents noms pour les fonctions statiques et virtuelles comment résoudre l'ambiguïté ici?

class Foo
{
public:
    static void f(bool f=true) { cout << "static";}
    virtual void f() { cout << "virtual";}
};
//somewhere
Foo::f(); //calls static f(), no ambiguity
ptr_to_foo->f(); //ambiguity

2) comment appeler implicitement OVERRIDE_STATIC_FUNCTIONS à l'intérieur de chaque constructeur?


+1 pour l'effort, même si je ne suis pas sûr que ce soit plus élégant que de simplement déléguer la fonctionnalité à un singleton avec des méthodes virtuelles.
einpoklum

1
@einpoklum, je peux penser à une situation où cela peut être préférable. Supposons que nous ayons beaucoup de code client qui appelle déjà des méthodes statiques. Le passage de méthodes statiques à un singleton avec des méthodes virtuelles nécessiterait des modifications du code client alors que la solution présentée ci-dessus n'est pas invasive.
Alsk

Le mot clé "virtual" n'est pas requis pour "Foo :: getTypeInformation" et "TypeKeeperImpl :: getTypeInformation".
bartolo-otrit

12

Bien qu'Alsk ait déjà donné une réponse assez détaillée, j'aimerais ajouter une alternative, car je pense que sa mise en œuvre améliorée est trop compliquée.

Nous commençons avec une classe de base abstraite, qui fournit l'interface pour tous les types d'objets:

class Object
{
public:
    virtual char* GetClassName() = 0;
};

Maintenant, nous avons besoin d'une mise en œuvre réelle. Mais pour éviter d'avoir à écrire à la fois les méthodes statiques et virtuelles, nous ferons en sorte que nos classes d'objets réelles héritent des méthodes virtuelles. Cela ne fonctionne évidemment que si la classe de base sait comment accéder à la fonction membre statique. Nous devons donc utiliser un modèle et lui passer le nom de la classe des objets réels:

template<class ObjectType>
class ObjectImpl : public Object
{
public:
    virtual char* GetClassName()
    {
        return ObjectType::GetClassNameStatic();
    }
};

Enfin, nous devons implémenter notre (nos) objet (s) réel (s). Ici, nous avons seulement besoin d'implémenter la fonction membre statique, les fonctions membres virtuelles seront héritées de la classe de modèle ObjectImpl, instanciées avec le nom de la classe dérivée, afin qu'elle accède à ses membres statiques.

class MyObject : public ObjectImpl<MyObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "MyObject";
    }
};

class YourObject : public ObjectImpl<YourObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "YourObject";
    }
};

Ajoutons du code à tester:

char* GetObjectClassName(Object* object)
{
    return object->GetClassName();
}

int main()
{
    MyObject myObject;
    YourObject yourObject;

    printf("%s\n", MyObject::GetClassNameStatic());
    printf("%s\n", myObject.GetClassName());
    printf("%s\n", GetObjectClassName(&myObject));
    printf("%s\n", YourObject::GetClassNameStatic());
    printf("%s\n", yourObject.GetClassName());
    printf("%s\n", GetObjectClassName(&yourObject));

    return 0;
}

Addendum (12 janvier 2019):

Au lieu d'utiliser la fonction GetClassNameStatic (), vous pouvez également définir le nom de la classe en tant que membre statique, même "inline", qui fonctionne depuis C ++ 11 (ne soyez pas effrayé par tous les modificateurs :)):

class MyObject : public ObjectImpl<MyObject>
{
public:
    // Access this from the template class as `ObjectType::s_ClassName` 
    static inline const char* const s_ClassName = "MyObject";

    // ...
};

11

C'est possible. Faites deux fonctions: statique et virtuelle

struct Object{     
  struct TypeInformation;
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain1();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain1();
  }
protected:
  static const TypeInformation &GetTypeInformationMain1(); // Main function
};

struct SomeObject : public Object {     
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain2();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain2();
  }
protected:
  static const TypeInformation &GetTypeInformationMain2(); // Main function
};

4
De plus, les méthodes statiques ne peuvent pas être const. Cela n'a tout simplement pas de sens, dans quel cas ne vont-ils pas muter?
David Rodríguez - dribeas

1
Il s'agit principalement de duplication de code. L'idée est que les sous-classes n'ont besoin que du membre statique const, pas de code d'accès.
einpoklum

8

Non, ce n'est pas possible, car les fonctions membres statiques n'ont pas de thispointeur. Et les membres statiques (fonctions et variables) ne sont pas vraiment des membres de classe en soi. Ils sont simplement appelés par ClassName::memberet adhèrent aux spécificateurs d'accès aux classes. Leur stockage est défini quelque part en dehors de la classe; le stockage n'est pas créé à chaque fois que vous instanciez un objet de la classe. Les pointeurs vers les membres de classe sont spéciaux dans la sémantique et la syntaxe. Un pointeur vers un membre statique est un pointeur normal à tous égards.

les fonctions virtuelles d'une classe ont besoin du thispointeur et sont très couplées à la classe, elles ne peuvent donc pas être statiques.


1
Seules les fonctions non statiques ont besoin d'un this pointeur. les fonctions statiques ne sont pas spécifiques à une instance et n'en auraient pas besoin. Donc - ce n'est pas une raison pour laquelle les membres statiques virtuels sont impossibles.
einpoklum

7

Eh bien, une réponse assez tardive, mais il est possible d'utiliser le modèle de modèle curieusement récurrent. Cet article de wikipedia contient les informations dont vous avez besoin et l'exemple sous polymorphisme statique est ce qui vous est demandé.


3

Je pense que ce que vous essayez de faire peut être fait grâce à des modèles. J'essaye de lire entre les lignes ici. Ce que vous essayez de faire est d'appeler une méthode à partir d'un code, où elle appelle une version dérivée mais l'appelant ne spécifie pas quelle classe. Exemple:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

void Try()
{
    xxx::M();
}

int main()
{
    Try();
}

Vous voulez que Try () appelle la version Bar de M sans spécifier Bar. La façon dont vous faites cela pour la statique est d'utiliser un modèle. Alors changez-le comme ceci:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

template <class T>
void Try()
{
    T::M();
}

int main()
{
    Try<Bar>();
}

1
Si vous indentez votre code de 4 espaces, vous pouvez le mettre en forme automatiquement. Sinon, je pense que vous pouvez utiliser la coche arrière pour atteindre le même objectif en ligne.
chollida

1
C'est l'évidence que j'ai ratée. Je vous remercie. Pourtant, les membres du pubis sont bizarres.
allesblinkt

M () n'est pas une fonction statique. comment s'appelle-t-il T :: M ()?
DDukDDak99

3

Non, la fonction membre statique ne peut pas être virtuelle. Car le concept virtuel est résolu au moment de l'exécution à l'aide de vptr, et vptr n'est pas un membre statique d'une classe. ne soyez pas virtuel.


2
Seules les méthodes virtuelles spécifiques à une instance nécessitent la table virtuelle des instances. Vous pourriez avoir une vtable statique - une par classe -. Et si vous voulez que les instances soient informées, pointez simplement de la vtable de l'instance vers la vtable de la statique de classe.
einpoklum

2

Ce n'est pas possible, mais c'est juste parce qu'une omission. Ce n'est pas quelque chose qui «n'a aucun sens» comme beaucoup de gens semblent le prétendre. Pour être clair, je parle de quelque chose comme ça:

struct Base {
  static virtual void sayMyName() {
    cout << "Base\n";
  }
};

struct Derived : public Base {
  static void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
  Derived::sayMyName(); // Also would work.
}

C'est quelque chose qui pourrait être implémenté à 100% (ce n'est tout simplement pas le cas), et je dirais quelque chose d'utile.

Considérez le fonctionnement des fonctions virtuelles normales. Supprimez les statics et ajoutez d'autres éléments et nous avons:

struct Base {
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
}

Cela fonctionne très bien et ce qui se passe, c'est que le compilateur crée deux tables, appelées VTables, et attribue des indices aux fonctions virtuelles comme celle-ci

enum Base_Virtual_Functions {
  sayMyName = 0;
  foo = 1;
};

using VTable = void*[];

const VTable Base_VTable = {
  &Base::sayMyName,
  &Base::foo
};

const VTable Derived_VTable = {
  &Derived::sayMyName,
  &Base::foo
};

Ensuite, chaque classe avec des fonctions virtuelles est augmentée avec un autre champ qui pointe vers sa VTable, de sorte que le compilateur les change fondamentalement pour être comme ceci:

struct Base {
  VTable* vtable;
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  VTable* vtable;
  void sayMyName() override {
    cout << "Derived\n";
  }
};

Alors que se passe-t-il réellement lorsque vous appelez b->sayMyName()? Fondamentalement ceci:

b->vtable[Base_Virtual_Functions::sayMyName](b);

(Le premier paramètre devient this .)

Ok, alors comment cela fonctionnerait-il avec des fonctions virtuelles statiques? Quelle est la différence entre les fonctions membres statiques et non statiques? La seule différence est que ces derniers reçoivent un thispointeur.

Nous pouvons faire exactement la même chose avec les fonctions virtuelles statiques - il suffit de supprimer le thispointeur.

b->vtable[Base_Virtual_Functions::sayMyName]();

Cela pourrait alors prendre en charge les deux syntaxes:

b->sayMyName(); // Prints "Base" or "Derived"...
Base::sayMyName(); // Always prints "Base".

Alors ignorez tous les opposants. Il ne du sens. Pourquoi n'est-il pas pris en charge alors? Je pense que c'est parce que cela a très peu d'avantages et pourrait même être un peu déroutant.

Le seul avantage technique par rapport à une fonction virtuelle normale est que vous n'avez pas besoin de passer this à la fonction, mais je ne pense pas que cela ferait une différence mesurable sur les performances.

Cela signifie que vous n'avez pas de fonction statique et non statique distincte pour les cas où vous avez une instance, et lorsque vous n'avez pas d'instance, mais il peut également être déroutant que ce soit vraiment "virtuel" lorsque vous utilisez l'appel d'instance.


0

Non, ce n'est pas possible, car les membres statiques sont liés au moment de la compilation, tandis que les membres virtuels sont liés au moment de l'exécution.


0

Premièrement, les réponses sont correctes que ce que l'OP demande est une contradiction dans les termes: les méthodes virtuelles dépendent du type d'exécution d'une instance; Les fonctions statiques ne dépendent pas spécifiquement d'une instance - juste d'un type. Cela dit, il est logique que les fonctions statiques retournent quelque chose de spécifique à un type. Par exemple, j'avais une famille de classes MouseTool pour le modèle State et j'ai commencé à avoir chacune une fonction statique renvoyant le modificateur de clavier qui allait avec; J'ai utilisé ces fonctions statiques dans la fonction d'usine qui a créé l'instance MouseTool correcte. Cette fonction a vérifié l'état de la souris par rapport à MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier (), etc., puis a instancié celui qui convient. Bien sûr, plus tard, je voulais vérifier si l'état était correct, donc je voulais écrire quelque chose comme "

Donc, si vous le souhaitez, vous voudrez peut-être revoir votre solution. Pourtant, je comprends le désir d'avoir des méthodes statiques, puis de les appeler dynamiquement en fonction du type dynamique d'une instance. Je pense que le modèle de visiteur peut vous donner ce que vous voulez. Cela vous donne ce que vous voulez. C'est un peu de code supplémentaire, mais cela pourrait être utile pour d'autres visiteurs.

Voir: http://en.wikipedia.org/wiki/Visitor_pattern pour le contexte.

struct ObjectVisitor;

struct Object
{
     struct TypeInformation;

     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v);
};

struct SomeObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

struct AnotherObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

Puis pour chaque objet concret:

void SomeObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // The compiler statically picks the visit method based on *this being a const SomeObject&.
}
void AnotherObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // Here *this is a const AnotherObject& at compile time.
}

puis définissez le visiteur de base:

struct ObjectVisitor {
    virtual ~ObjectVisitor() {}
    virtual void visit(const SomeObject& o) {} // Or = 0, depending what you feel like.
    virtual void visit(const AnotherObject& o) {} // Or = 0, depending what you feel like.
    // More virtual void visit() methods for each Object class.
};

Puis le visiteur concret qui sélectionne la fonction statique appropriée:

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) {
        result = SomeObject::GetTypeInformation();
    }
    virtual void visit(const AnotherObject& o) {
        result = AnotherObject::GetTypeInformation();
    }
    // Again, an implementation for each concrete Object.
};

enfin, utilisez-le:

void printInfo(Object& o) {
    ObjectVisitorGetTypeInfo getTypeInfo;
    Object::TypeInformation info = o.accept(getTypeInfo).result;
    std::cout << info << std::endl;
}

Remarques:

  • Constness laissé comme exercice.
  • Vous avez renvoyé une référence à partir d'un statique. À moins d'avoir un singleton, c'est discutable.

Si vous souhaitez éviter les erreurs de copier-coller où l'une de vos méthodes de visite appelle la mauvaise fonction statique, vous pouvez utiliser une fonction d'assistance basée sur un modèle (qui ne peut pas elle-même être virtuelle) pour votre visiteur avec un modèle comme celui-ci:

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) { doVisit(o); }
    virtual void visit(const AnotherObject& o) { doVisit(o); }
    // Again, an implementation for each concrete Object.

  private:
    template <typename T>
    void doVisit(const T& o) {
        result = T::GetTypeInformation();
    }
};

les méthodes statiques virtuelles, si elles existaient, ne dépendraient de rien dans une instance - mais l'instance aurait besoin de connaître son type pour les invoquer. Cela peut être résolu par un compilateur (par exemple en utilisant une structure de données unique par classe avec des pointeurs vers des méthodes et des membres statiques virtuels.) Ce n'est certainement pas une contradiction dans les termes.
einpoklum

Que ce soit ou non une contradiction dans les termes est une question de sémantique. On pourrait imaginer C ++ permettant d'appeler des statiques à partir d'une instance (par exemple, Foo foo; ... foo::bar();au lieu de Foo::bar();). Ce n'est pas différent, decltype(foo)::bar();mais ce serait encore une fois lié statiquement. L'approche du visiteur semble être un moyen raisonnable d'obtenir ce comportement sans simplement faire de la méthode statique une méthode virtuelle const.
Ben

0

Avec c ++, vous pouvez utiliser l'héritage statique avec la méthode crt. Pour l'exemple, il est largement utilisé sur les modèles de fenêtres atl & wtl.

Voir https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Pour être simple, vous avez une classe qui est basée sur elle-même comme class myclass: public myancestor. À partir de ce point, la classe myancestor peut désormais appeler votre fonction statique T :: YourImpl.


-1

Peut-être que vous pouvez essayer ma solution ci-dessous:

class Base {
public:
    Base(void);
    virtual ~Base(void);

public:
    virtual void MyVirtualFun(void) = 0;
    static void  MyStaticFun(void) { assert( mSelf != NULL); mSelf->MyVirtualFun(); }
private:
    static Base* mSelf;
};

Base::mSelf = NULL;

Base::Base(void) {
    mSelf = this;
}

Base::~Base(void) {
    // please never delete mSelf or reset the Value of mSelf in any deconstructors
}

class DerivedClass : public Base {
public:
    DerivedClass(void) : Base() {}
    ~DerivedClass(void){}

public:
    virtual void MyVirtualFun(void) { cout<<"Hello, it is DerivedClass!"<<endl; }
};

int main() {
    DerivedClass testCls;
    testCls.MyStaticFun(); //correct way to invoke this kind of static fun
    DerivedClass::MyStaticFun(); //wrong way
    return 0;
}

Ouais, je sais, 4 ans. Expliquer le -score pour ceux qui ne veulent pas lire le code avec autant de détails. Base::mSelffait référence à l'instance la plus récemment construite de toute classe dérivée, même si cette instance a été détruite . donc class D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */ce qui n'est PAS ce que l'on veut.
Jesse Chisholm

-3

Comme d'autres l'ont dit, il y a 2 informations importantes:

  1. il n'y a pas de thispointeur lors d'un appel de fonction statique et
  2. le thispointeur pointe vers la structure où la table virtuelle, ou thunk, est utilisée pour rechercher la méthode d'exécution à appeler.

Une fonction statique est déterminée au moment de la compilation.

J'ai montré cet exemple de code dans les membres statiques C ++ en classe ; cela montre que vous pouvez appeler une méthode statique avec un pointeur nul:

struct Foo
{
    static int boo() { return 2; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Foo* pFoo = NULL;
    int b = pFoo->boo(); // b will now have the value 2
    return 0;
}

6
Techniquement, il s'agit d'un comportement indéfini. Vous ne pouvez pas déférer un pointeur nul pour quelque raison que ce soit. Les seules choses que vous pouvez faire avec un pointeur nul est a) de lui affecter un autre pointeur et b) de le comparer avec un autre pointeur.
KeithB

1
De plus, vous ne pouvez le comparer que pour l'égalité (ou l'inégalité_ avec un autre pointeur, sans ordre. C'est-à-dire p < null, p >= nulletc., sont tous non définis également.
Pavel Minaev

1
@KeithB - ​​Pour être complet, vous pouvez également appeler en toute sécurité delete sur un pointeur nul.
Steve Rowe
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.