Question

Ceci est probablement mieux illustré avec un exemple de code.Ce qui suit ne parvient pas à compiler avec g++ :

struct Base {
};

struct Derived : public Base {
};

struct Container {
    Derived data_;
};

int main(void) {
    Base Container::*ptr = &Container::data_;
}

J'obtiens l'erreur suivante : invalid conversion from 'Derived Container::*' to Base Container::*'.Est-ce que cela n'est pas autorisé par la langue ?Est-ce un bug du compilateur ?Est-ce que j'utilise la mauvaise syntaxe ?

S'il vous plaît, aidez-moi !

Quelques informations sur les raisons pour lesquelles j'essaie de faire cela :J'ai plusieurs éléments de données membres que je souhaite utiliser principalement comme types dérivés, mais je souhaite pouvoir les renseigner via un code commun.Les données arriveront dans un ordre arbitraire et auront une étiquette de chaîne que j'utiliserais pour sélectionner les données de membre appropriées à remplir.J'avais l'intention de créer un std::map<std::string, Base Container::*> pour attribuer des données à chaque membre via une interface commune.J'aimerais éviter d'avoir un géant if else construire pour trouver les bonnes données de membre.

Était-ce utile?

La solution

Ce n'est pas un bug du compilateur, vous ne pouvez pas faire ça.(Mais vous pouvez attribuer un Base ::* à un Derived ::*).

Je ne vois aucune bonne raison pour la limitation (sauf que pour gérer le cas d'héritage multiple, cela compliquerait encore plus la représentation d'un pointeur de membre).

Autres conseils

Il y a beaucoup de réponses assez complexes, certaines mal expliquées et quelques mauvaises réponses dans ce fil.

Mais le problème, me semble-t-il, c'est qu'il n'existe tout simplement pas de Base membre à l'intérieur Container -- Il y a un Derived membre.Vous ne pouvez pas faire ceci :

Base Container::*ptr = &Container::data_;

...pour la même raison, vous ne pouvez pas faire ceci :

int a;
long* pl = &a;

Dans le deuxième exemple, l'objet n'est pas un long, c'est un int.De même, dans le premier exemple, l'objet n'est pas un Base, c'est un Derived.

En guise de point éventuellement tangentiel, il me semble que ce que vous voulez vraiment faire, c'est avoir Base être une classe abstraite et avoir Container avoir un Base* Plutôt qu'un Derived membre.

Les pointeurs vers les membres en C++ ne le sont pas vraiment pointeurs mais plutôt compensations à un membre donné et sont spécifiques au type, donc ce que vous essayez de faire n'est pas vraiment pris en charge.

Voici une discussion décente ici sur Stackoverflow C++ :Pointeur vers le membre de données de classe.

Il vous suffit d'écrire :

Base* ptr = &container.data_;

mais container doit être un exemple de Container, vous devez donc créer une variable de ce type quelque part.

Vous ne pouvez pas convertir C::*A en C::*B même s'il existe une conversion possible entre A et B.

Cependant, vous pouvez faire ce:

struct Base
{
    virtual ~Base() {}
    virtual void foo() { std::cout << "Base::foo()\n"; }
};

struct Derived : Base
{
    void foo() { std::cout << "Derived::foo()\n"; }
};

struct Bar
{
    Base* x;

    Bar() : x(new Derived) {}
};

int main()
{
    Bar b;
    Base* Bar::*p = &Bar::x;
    (b.*p)->foo();
}

Vous auriez à static_cast pour effectuer cette conversion comme vu dans 5.3.9/9.La raison en est qu'il agit comme un static_cast du pointeur d'objet parent au pointeur d'objet enfant.En d’autres termes, placer un pointeur vers un membre dérivé dans un pointeur vers un membre parent vous permettrait éventuellement d’accéder à un membre dérivé inexistant à partir d’un objet parent ou d’un pointeur.Si la norme le permettait automatiquement, il serait facile de se tromper et d'essayer d'accéder à un membre enfant d'une classe qui n'est pas du type enfant approprié (qui contient ledit membre).

Sans plus d'informations, il semble que vous ayez besoin d'une interface constructeur/ensemble différente/meilleure dans votre Base classe plutôt que d’essayer d’utiliser des pointeurs vers des membres ici.

Je pense que ce que vous voulez, c'est un "conteneur", c'est-à-dire une structure qui n'a que des pointeurs :

struct Container{
    Base* derivedAdata_;
    Base* derivedBdata_;
    ...
};

Maintenant, chacun des membres que vous savez être d'un type spécifique (c'est-à-dire DerivedA, DerivedB, etc.) afin que vous puissiez les réduire plus tard.

Mais vous recevez d’abord des données (dans un ordre arbitraire), mais avec un nom de chaîne, vous devriez donc avoir une carte :

std::map<std::string, Base* Container::*>

Et vous devez déjà avoir rempli la carte :

myMap["DerivedA"] = &Container::derivedAdata;
...

Les données arrivent maintenant et vous commencez à remplir le conteneur :

instance.*(myMap[key]) = factory(key, data);

myMap[key] choisit le bon membre du conteneur et factory(key,data) crée des instances.

au fait, vous pourriez de toute façon avoir simplement une carte comme conteneur :std::map<std::string, Base*>

Concernant le problème d'origine, vous pouvez le faire en utilisant un pointeur vers des fonctions, au lieu d'introduire des classes de base.

class Container {
public:
  void set(std::string const& label, std::string const& value);

  void setName(std::string const& value) { _name = value; }
  void setAge(std::string const& age) {
    _age = boost::lexical_cast<size_t>(age);
  }

private:
  std::string _name;
  size_t _age;
};

Comment mettre en œuvre set alors ?

// container.cpp
typedef void (Container::*SetterType)(std::string const&);
typedef std::map<std::string, SetterType> SettersMapType;

SettersMapType SettersMap =
  boost::assign::map_list_of("name", &Container::setName)
                            ("age", &Container::setAge);

void Container::set(std::string const& label, std::string const& value) {
  SettersMapType::const_iterator it = SettersMap.find(label);
  if (it == SettersMap.end()) { throw UnknownLabel(label); }

  SetterType setter = it->second;
  (this->*setter)(value);
}
struct Container {
   Derived data_; 
};  

int main(void) 
{
   Base Container::*ptr = &Container::data_;
} 

Le premier problème est que Container n'a pas de membre appelé ptr

Container container_object;
Base *ptr = container_object.data_;

Travaillerait.Notez qu'il doit y avoir un objet conteneur pour créer le membre data_ et qu'il devra être rendu public.

L'alternative serait que dérivé::data_ soit un membre statique.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top