Implémentation d’arguments nommés C ++ avec des classes dérivées

J’essaie de créer un constructeur semblable à des arguments nommés pour certaines des classes d’un projet.

Je le fais en définissant un proxy de classe qui contiendra les arguments et transmettra une instance de ce proxy au constructeur de mes classes.

Tout a bien fonctionné jusqu’à ce que je devais suivre l’un de mes cours.

En gros, je me suis dit: je vais tirer le nouveau proxy de classe dérivée du proxy de classe de base. Cela fonctionne aussi, mais seulement si j’utilise uniquement les arguments de la classe proxy dérivés.

Voici un exemple puisqu’il est plus facile à comprendre:

class Person { public: class PersonArgs { public: const std::ssortingng& Name() const { return _name; } PersonArgs& Name(const std::ssortingng& name) { _name = name; return *this; } const std::ssortingng& Surname() const { return _surname; } PersonArgs& Surname(const std::ssortingng& surname) { _surname = surname; return *this; } protected: std::ssortingng _name; std::ssortingng _surname; } public: Person() : _name("") , _surname("") { } Person(const PersonArgs& args) : _name(args.Name()) , _surname(args.Surname()) { } protected: std::ssortingng _name; std::ssortingng _surname; } class PersonEx : public Person { public: class PersonExArgs : public Person::PersonArgs { public: const std::ssortingng& Address() const { return _address; } PersonExArgs& Address(const std::ssortingng& address) { _address = address; return *this; } protected: std::ssortingng _address; } public: PersonEx() : _address("") { } PersonEx(const PersonExArgs& args) : Person(args) , _address(args.Address()) { } protected: std::ssortingng _address; } int main(int argc, char** argv) { // This is ok since PersonExArgs::Address returns a PersonExArgs& PersonEx* p1 = new PersonEx(PersonEx::PersonExArgs().Address("example")); // This won't work since PersonExArgs::Name returns a PersonArgs& PersonEx* p2 = new PersonEx(PersonEx::PersonExArgs().Address("example").Name("Mark")); } 

Fondamentalement, puisque je chaîne les arguments renvoyant une référence à l’instance de la classe proxy lorsque je définis un argument, il se rompt lors de son utilisation à partir d’une classe proxy dérivée, car il renverrait une référence à la classe proxy de base et non à la classe dérivée, ne permettant pas me d’accéder aux arguments de proxy dérivés et de ne pas le transmettre au constructeur de la classe dérivée.

Quelqu’un a une idée sur la façon de résoudre ce problème?

Peut-être recherchez-vous les types de retour covariants .
Voir ici pour plus de détails.


Vous pouvez définir PersonArgs comme (notez virtual mots-clés virtual placés autour):

 class PersonArgs { public: const std::ssortingng& Name() const { return _name; } virtual PersonArgs& Name(const std::ssortingng& name) { _name = name; return *this; } const std::ssortingng& Surname() const { return _surname; } virtual PersonArgs& Surname(const std::ssortingng& surname) { _surname = surname; return *this; } protected: std::ssortingng _name; std::ssortingng _surname; }; 

Définissez ensuite PersonExArgs tant que ( override note et type de retour covariant):

 class PersonExArgs : public Person::PersonArgs { public: const std::ssortingng& Address() const { return _address; } PersonExArgs& Address(const std::ssortingng& address) { _address = address; return *this; } PersonExArgs& Name(const std::ssortingng& name) override { PersonArgs::Name(name); return *this; } PersonExArgs& Surname(const std::ssortingng& surname) override { PersonArgs::Surname(surname); return *this; } protected: std::ssortingng _address; }; 

C’est probablement ennuyeux car vous devez redéfinir chaque fonction de la classe de base, mais cela fonctionne bien.
Voyez-le en marche sur wandbox .

La solution la plus courante à ce problème est le modèle de modèle curieusement récurrent (CRTP):

 template  class PersonArgs { public: const std::ssortingng& Name() const { return _name; } Derived& Name(const std::ssortingng& name) { _name = name; return static_cast(*this); } const std::ssortingng& Surname() const { return _surname; } Derived& Surname(const std::ssortingng& surname) { _surname = surname; return static_cast(*this); } protected: std::ssortingng _name; std::ssortingng _surname; }; ... class PersonExArgs : public Person::PersonArgs { public: const std::ssortingng& Address() const { return _address; } PersonExArgs& Address(const std::ssortingng& address) { _address = address; return *this; } protected: std::ssortingng _address; }; 

Dans votre cas, vous pouvez le combiner avec une autre classe de base pour nettoyer l’interface:

 class Person { class PersonArgsBase { public: const std::ssortingng& Name() const { return _name; } const std::ssortingng& Surname() const { return _surname; } protected: std::ssortingng _name; std::ssortingng _surname; }; template  class PersonArgs : public PersonArgsBase { Derived& Name(const std::ssortingng& name) { _name = name; return static_cast(*this); } Derived& Surname(const std::ssortingng& surname) { _surname = surname; return static_cast(*this); } }; ... }; class PersonEx { class PersonExArgs : public Person::PersonArgs { ... }; };