web-dev-qa-db-fra.com

Une classe d'énumération C ++ peut-elle avoir des méthodes?

J'ai une classe enum avec deux valeurs et je veux créer une méthode qui reçoit une valeur et retourne l'autre. Je souhaite également maintenir la sécurité de type (c'est pourquoi j'utilise enum class au lieu de enums).

http://www.cplusplus.com/doc/tutorial/other_data_types/ ne mentionne rien sur les méthodes Cependant, j'avais l'impression que tout type de classe peut avoir des méthodes.

108
octavian

Non, ils ne peuvent pas.

Je peux comprendre que le enum class Une partie des enums fortement typés de C++ 11 peut sembler impliquer que votre enum possède également des traits class, mais ce n'est pas le cas. Mon hypothèse est que le choix des mots-clés a été inspiré par le modèle que nous utilisions avant C++ 11 pour obtenir des énumérations étendues:

class Foo {
public:
  enum {BAR, BAZ};
};

Cependant, c'est juste la syntaxe. Encore, enum class n'est pas un class.

95
Stefano Sanfilippo

Bien que la réponse selon laquelle "vous ne pouvez pas" soit techniquement correcte, je pense que vous pourrez peut-être obtenir le comportement que vous recherchez en utilisant l'idée suivante:

J'imagine que vous voulez écrire quelque chose comme:

Fruit f = Fruit::Strawberry;
f.IsYellow();

Et vous espériez que le code ressemble à ceci:

enum class Fruit : uint8_t
{
  Apple, 
  Pear,
  Banana,
  Strawberry,

  bool IsYellow() { return this == Banana; }
};

...

Mais bien sûr, cela ne fonctionne pas, car les enums ne peuvent pas avoir de méthodes (et 'ceci' ne veut rien dire dans le contexte ci-dessus)

Toutefois, si vous utilisez l'idée d'une classe normale contenant une énumération de non-classe et une variable de membre unique contenant une valeur de ce type, vous pouvez être extrêmement proche de la sécurité syntaxe/comportement/type souhaitée. c'est à dire.:

class Fruit
{
public:
  enum Value : uint8_t
  {
    Apple,
    Pear,
    Banana,
    Strawberry
  };

  Fruit() = default;
  constexpr Fruit(Value aFruit) : value(aFruit) { }

#if Enable switch(fruit) use case:
  operator Value() const { return value; }  // Allow switch and comparisons.
                                            // note: Putting constexpr here causes
                                            // clang to stop warning on incomplete
                                            // case handling.
  explicit operator bool() = delete;        // Prevent usage: if(fruit)
#else
  constexpr operator==(Fruit a) const { return value == a.value; }
  constexpr operator!=(Fruit a) const { return value != a.value; }
#endif

  constexpr bool IsYellow() const { return value == Banana; }

private:
  Value value;
};

Maintenant vous pouvez écrire:

Fruit f = Fruit::Strawberry;
f.IsYellow();

Et le compilateur empêchera des choses comme:

Fruit f = 1;  // Compile time error.

Vous pouvez facilement ajouter des méthodes telles que:

Fruit f("Apple");

et

f.ToString();

peut être pris en charge.

41
jtlim

Se concentrer sur la description de la question au lieu du titre, une réponse possible est

struct LowLevelMouseEvent {
    enum Enum {
        mouse_event_uninitialized = -2000000000, // generate crash if try to use it uninitialized.
        mouse_event_unknown = 0,
        mouse_event_unimplemented,
        mouse_event_unnecessary,
        mouse_event_move,
        mouse_event_left_down,
        mouse_event_left_up,
        mouse_event_right_down,
        mouse_event_right_up,
        mouse_event_middle_down,
        mouse_event_middle_up,
        mouse_event_wheel
    };
    static const char* ToStr (const type::LowLevelMouseEvent::Enum& event)
    {
        switch (event) {
            case mouse_event_unknown:         return "unknown";
            case mouse_event_unimplemented:   return "unimplemented";
            case mouse_event_unnecessary:     return "unnecessary";
            case mouse_event_move:            return "move";
            case mouse_event_left_down:       return "left down";
            case mouse_event_left_up:         return "left up";
            case mouse_event_right_down:      return "right down";
            case mouse_event_right_up:        return "right up";
            case mouse_event_middle_down:     return "middle down";
            case mouse_event_middle_up:       return "middle up";
            case mouse_event_wheel:           return "wheel";
            default:
                Assert (false);
                break;
        }
        return "";
    }
};
16
Márkus Attila

Comme mentionné dans le autre réponse , non. Même enum class n'est pas une classe.


Habituellement, le besoin d'avoir des méthodes pour un enum résulte de la raison pour laquelle ce n'est pas un régulier (juste incrémenté) enum, mais une sorte de définition au niveau du bit des valeurs à masquer ou qui nécessitent d'autres opérations arithmétiques sur les bits:

enum class Flags : unsigned char {
    Flag1 = 0x01 , // Bit #0
    Flag2 = 0x02 , // Bit #1
    Flag3 = 0x04 , // Bit #3
    // aso ...
}

// Sets both lower bits
unsigned char flags = (unsigned char)(Flags::Flag1 | Flags::Flag2);

// Set Flag3
flags |= Flags::Flag3;

// Reset Flag2
flags &= ~Flags::Flag2;

Évidemment, on songe à encapsuler les opérations nécessaires pour rétablir/définir un seul/groupe de bits, par ex. Une valeur de masque binaire ou même des opérations indexées sur les bits seraient utiles pour manipuler un tel ensemble de "drapeaux".

Le c ++ 11struct/classspécification permet simplement une meilleure définition des valeurs d'enum pour l'accès. Ni plus ni moins!

Les moyens de sortir de la restriction que vous ne pouvez pas déclarer sont des méthodes pour enum (classes) , soit d'utiliser un std::bitset (classe wrapper), ou un champ de bits union .

unions, et de telles unions de champs binaires peuvent avoir des méthodes (voir ici pour les restrictions!).

J'ai un exemple, comment convertir les valeurs de masque de bits (comme indiqué ci-dessus) en index de bits correspondants, utilisables le long d'un std::bitset ici: BitIndexConverter.hpp
J'ai trouvé cela très utile pour améliorer la lisibilité de certains algorithmes basés sur la décision 'drapeau'.

4
πάντα ῥεῖ

Il y a un capacité assez compatible (§) pour refactoriser une enum dans une classe sans avoir à réécrire votre code, ce qui signifie que effectivement vous pouvez faites ce que vous demandiez de faire sans trop de montage.

(§) comme le souligne ElementW dans un commentaire, le code dépendant de type_traits ne fonctionnera pas, ainsi, par exemple. on ne peut pas utiliser auto, etc. Il y a peut-être un moyen de gérer ce genre de choses, mais au final, on convertit une énumération en classe et il est toujours erroné de renverser C++

le enum struct et enum class _ les spécifications concernent la portée, donc n'en faites pas partie.

Votre enum original est par exemple 'pet' (c'est à titre d'exemple seulement!).

enum pet { 
    fish, cat, dog, bird, rabbit, other 
};

(1) Vous modifiez cela, par exemple, petEnum (afin de le masquer de votre code existant).

enum petEnum { 
    fish, cat, dog, bird, rabbit, other 
};

(2) Vous ajoutez une nouvelle déclaration de classe en dessous (nommée avec l'énumération d'origine)

class pet {
    private:
        petEnum value;
        pet() {}

    public:
        pet(const petEnum& v) : value{v} {} //not explicit here.
        operator petEnum() const { return value; }
        pet& operator=(petEnum v) { value = v; return *this;}
        bool operator==(const petEnum v) const { return value == v; }
        bool operator!=(const petEnum v) const { return value != v; }
 //     operator std::string() const;

};

(3) Vous pouvez maintenant ajouter les méthodes de classe de votre choix à votre classe d'animaux domestiques. par exemple. un opérateur de chaîne

    pet::operator std::string() const {
        switch (value) {
            case fish: return "fish";
            case cat:  return "cat";
            case dog:  return "dog";
            case bird: return "bird";
            case rabbit: return "rabbit";
            case other: return "Wow. How exotic of you!";
        }
    }

Maintenant, vous pouvez utiliser par exemple std :: cout ...

int main() {
    pet myPet = rabbit;
    if(myPet != fish) {
        cout << "No splashing! ";
    }
    std::cout << "I have a " << std::string(myPet) << std::endl;
    return 0;
}
2
Konchog