Existe-t-il un moyen en C++ d'étendre/"hériter" des enumérations ?
Par exemple :
enum Enum {A,B,C};
enum EnumEx : public Enum {D,E,F};
ou au moins définir une conversion entre eux ?
Existe-t-il un moyen en C++ d'étendre/"hériter" des enumérations ?
Par exemple :
enum Enum {A,B,C};
enum EnumEx : public Enum {D,E,F};
ou au moins définir une conversion entre eux ?
Non, il n'y en a pas.
enum
est vraiment le pauvre élément en C++, et c'est bien sûr malheureux.
Même la enum classe
introduite en C++0x ne résout pas ce problème d'extensibilité (bien qu'ils fassent certaines choses pour la sécurité des types au moins).
Le seul avantage de enum
est qu'ils n'existent pas : ils offrent une certaine sécurité des types sans imposer de surcharge à l'exécution car ils sont directement substitués par le compilateur.
Si vous voulez un tel élément, vous devrez travailler vous-même :
MyEnum
, qui contient un int (basiquement)vous pouvez maintenant étendre votre classe (en ajoutant des constructeurs nommés) à volonté...
C'est une solution de contournement cependant, je n'ai jamais trouvé de moyen satisfaisant de traiter une énumération...
J'ai résolu de cette manière :
typedef enum
{
#include "NetProtocols.def"
} eNetProtocols, eNP;
Bien sûr, si vous ajoutez un nouveau protocole de réseau dans le fichier NetProtocols.def, vous devez recompiler, mais au moins il est extensible.
"NetProtocols.def" contiendra uniquement les noms des champs :
HTTP,
HTTPS,
FTP
Oui, si vous souhaitez que la définition enum soit dans une bibliothèque, vous ne pourriez pas le faire...
@macro cela ne fonctionne pas pour moi. Pourriez-vous s'il vous plaît lister quelques contenus de NetProtocols.def
Si vous étiez capable de créer une sous-classe d'une énumération, cela devrait fonctionner dans l'autre sens.
L'ensemble des instances dans une sous-classe est un sous-ensemble des instances dans la super-classe. Pensez à l'exemple standard de "Forme". La classe Forme représente l'ensemble de toutes les formes. La classe Cercle, sa sous-classe, représente le sous-ensemble des formes qui sont des cercles.
Donc, pour être cohérent, une sous-classe d'une énumération devrait contenir un sous-ensemble des éléments de l'énumération dont elle hérite.
(Et non, C++ ne supporte pas cela.)
C'est une interprétation très spécifique de l'héritage, qui ne s'applique pas nécessairement à toutes les utilisations de l'héritage. Je ne pense pas que cela explique vraiment pourquoi un compilateur ne pourrait pas prendre en charge enum EnumEx : public Enum {D,E,F}; de sorte qu'un EnumEx puisse être passé à une fonction attendant un Enum.
@jon: parce que cela violerait le principe de substitution de Liskov : D serait "un" Enum (car il en hérite), mais n'aurait aucune des valeurs valides d'un Enum. Contradiction. les enums sont conçus pour être des "types énumérés" - le but est que vous définissiez le type en définissant tous les objets valides de ce type (dans le cas de C/C++, en réalité la correspondance des valeurs répertoriées à tous les types valides est un peu étrange, et implique le type utilisé pour représenter l'énumération). Il s'agit peut-être d'une interprétation très spécifique de l'héritage, mais c'est une bonne interprétation, et autant que je sache, cela informe la conception de C++.
Comme exemple particulier où cela pourrait poser problème, supposez que je définisse un enum A {1, 65525};
et que le compilateur décide d'utiliser un entier non signé de 16 bits pour le représenter. Maintenant, supposez que je définisse enumEx : public Enum { 131071 };
. Il n'y a aucun moyen pour cet objet de type EnumEx d'être passé en tant qu'instance de Enum, cela reviendrait en effet à une coupure. Oups. C'est pourquoi vous avez besoin de pointeurs en C++ pour faire de la polymorphie à l'exécution. Je suppose que C++ pourrait faire en sorte que chaque énumération ait la taille de la plus grande énumération possible. Mais conceptuellement parlant, la valeur 131071 ne devrait pas être une instance valide de Enum.
http://www.codeproject.com/KB/cpp/InheritEnum.aspx passe en revue une méthode pour créer une énumération étendue.
Créez InheritEnum.h:
// -- InheritEnum.h
template
class InheritEnum
{
public:
InheritEnum() {}
InheritEnum(EnumT e)
: enum_(e)
{}
InheritEnum(BaseEnumT e)
: baseEnum_(e)
{}
explicit InheritEnum( int val )
: enum_(static_cast(val))
{}
operator EnumT() const { return enum_; }
private:
// Note - la valeur est déclarée comme une union principalement pour aider au débogage. Si
// la union n'est pas souhaitée et que vous avez d'autres méthodes de débogage, modifiez-la
// en EnumT ou en base de EnumT et faites une conversion pour le constructeur qui accepte BaseEnumT.
union
{
EnumT enum_;
BaseEnumT baseEnum_;
};
};
Et ensuite l'utiliser:
enum Fruit { Orange, Mango, Banana };
enum NewFruits { Apple, Pear };
typedef InheritEnum< NewFruit, Fruit > MyFruit;
void consume(MyFruit myfruit);
Votre expérience peut varier.
Une méthode très dangereuse. Si deux enums mis ensemble utilisent la même valeur, cela les confondra silencieusement.
@dspeyer cela apporte tout de même une idée intéressante à l'ensemble du débat. votre solution est une progéniture de cela.
Les réponses ne doivent pas être seulement des liens. (Et ce code a un comportement indéfini dans sa "conversion".)
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
2 votes
J'ai posté une réponse c++11 à stackoverflow.com/questions/14503620/extending-enum-types/…
0 votes
Il y a un article très intéressant, avec du code, ici: codeproject.com/Articles/32000/…