Comment utiliser les énumérations en C ++

218

Supposons que nous ayons un enumcomme ceci:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};

Je veux créer une instance de ceci enumet l'initialiser avec une valeur appropriée, alors je fais:

Days day = Days.Saturday;

Maintenant, je veux vérifier ma variable ou mon instance avec une enumvaleur existante , alors je fais:

if (day == Days.Saturday)
{
    std::cout << "Ok its Saturday";
}

Ce qui me donne une erreur de compilation:

erreur: expression primaire attendue avant '.' jeton

Donc, pour être clair, quelle est la différence entre dire:

if (day == Days.Saturday) // Causes compilation error

et

if (day == Saturday)

?

À quoi ces deux se réfèrent-ils réellement, dans le sens où l'un est OK et l'autre provoque une erreur de compilation?

Rika
la source
4
Je sais, je veux savoir pourquoi ça me donne l'erreur!
Rika
1
C'est mercredi ici. Vous avez trop d'erreurs de syntaxe pour le compilateur C ++. À partir de «Enum».
Öö Tiib
1
@Hossein, Parce que les énumérations ne sont pas la même syntaxe (et sémantique) dans les deux langues. La première chose que je fais après avoir obtenu une erreur en essayant d'utiliser une fonctionnalité dans une nouvelle langue est de rechercher la syntaxe (ou si c'est possible) dans cette langue.
chris
@chris: Je sais, je fais exactement la même chose.J'espère que j'ai obtenu ma réponse.J'ai également mis à jour la question pour être plus claire.Merci au fait;)
Rika
17
" pour autant que je sache, la déclaration et l'utilisation des énumérations dans ces deux langues sont les mêmes. ". Voilà votre problème, juste là. C # n'est pas le même langage que C ++. En particulier, ils ont une syntaxe différente pour les énumérations.
Robᵩ

Réponses:

350

Ce code est faux:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days.Saturday;
if (day == Days.Saturday)

Parce que ce Daysn'est pas une portée, ni un objet. C'est un type. Et les types eux-mêmes n'ont pas de membres. Ce que vous avez écrit est l'équivalent de std::string.clear. std::stringest un type, vous ne pouvez donc pas l'utiliser .. Vous utilisez .sur une instance d'une classe.

Malheureusement, les énumérations sont magiques et donc l'analogie s'arrête là. Parce qu'avec une classe, vous pouvez faire std::string::clearpour obtenir un pointeur sur la fonction membre, mais en C ++ 03, Days::Sundayn'est pas valide. (Ce qui est triste). C'est parce que C ++ est (quelque peu) rétrocompatible avec C, et C n'avait pas d'espaces de noms, donc les énumérations devaient être dans l'espace de noms global. La syntaxe est donc simplement:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday)

Heureusement, Mike Seymour observe que cela a été résolu en C ++ 11. Changez enumpour enum classet il obtient sa propre portée; Days::Sundayn'est donc pas seulement valable, mais c'est le seul moyen d'y accéder Sunday. Jours heureux!

Mooing Duck
la source
254
Heureusement, votre plainte a été traitée en C ++ 11. Changez enumpour enum classet il obtient sa propre portée; Days::Sundayn'est donc pas seulement valable, mais c'est le seul moyen d'y accéder Sunday. Jours heureux!
Mike Seymour
11
Je dois aimer les messages d'erreur C ++ ... ils prouvent que le langage est trop lourd pour même donner de bons commentaires. Je suppose qu'une «expression primaire» est un objet ou une portée ou une autre chose qui n'est PAS un type. Peut-être qu'un type est une «expression secondaire». Et ce qu'un développeur C ++ pourrait appeler un «opérateur point», le compilateur C ++ ne peut appeler qu'un «jeton». Quand il devient difficile de comprendre les messages d'erreur, il y a quelque chose qui ne va pas avec la langue, je pense.
Travis
4
@Travis: en.cppreference.com/w/cpp/language/… . Une expression principale n'est que la première chose dans une expression, généralement un nom ou une variable ou un littéral. Quant à la deuxième partie, je ne vois pas de grande différence entre '.' tokenet dot operator, à part que c'est un jeton et non un opérateur, et il montre le symbole exact, plutôt qu'un nom.
Mooing Duck
@Mike Seymour J'ai essayé d'accéder aux énumérations sans les opérateurs de résolution de portée sur un tas de compilateurs, et cela semble fonctionner. Vous avez dit qu'à partir de C ++ 11, c'est le seul moyen, pour une raison quelconque, je peux simplement accéder aux valeurs d'énumération en tant que globaux, pas besoin de ::
Zebrafish
1
@TitoneMaurice: Si vous en avez un enum, vous ne pouvez utiliser aucune étendue, ou l'étendue globale ( ::Saturday). Si vous en avez un enum class(ce qui est très différent), vous devez l' utiliser Days::Saturday.
Mooing Duck
24

Ce sera suffisant pour déclarer votre variable enum et la comparer:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday) {
    std::cout << "Ok its Saturday";
}
mathématicien1975
la source
pourquoi est-ce mal de dire si (jour == jours.samedi)? ils doivent être les mêmes, alors pourquoi le compilateur s'en plaint-il?
Rika
1
@Hossein les valeurs déclarées dans votre énumération ne se comportent pas comme des variables membres de classe ou de structure. Ce n'est pas la bonne syntaxe à utiliser
mathématicien1975
2
@Hossein: car ce Daysn'est pas une portée, ni un objet. C'est un type. Et les types eux - mêmes n'ont pas de membres. std::string.clearéchoue également à compiler pour la même raison.
Mooing Duck
8
@Hossein: Parce que ce n'est pas ainsi que fonctionnent les énumérations en C ++. Les énumérations non étendues placent leurs valeurs dans l'espace de noms environnant; les scope ( enum class, nouvelles en 2011) ont leur propre champ, et sont accessibles en utilisant l'opérateur de portée Days::Saturday. L'opérateur d'accès aux membres ( .) est uniquement utilisé pour accéder aux membres de la classe.
Mike Seymour
@MooingDUck et MikeSeymour L'un de vous posterait-il votre réponse comme réponse? car c'est exactement ce que je cherchais en posant cette question;)
Rika
22

Une grande partie de cela devrait vous donner des erreurs de compilation.

// note the lower case enum keyword
enum Days { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday };

Maintenant, Saturday, Sunday, etc. peuvent être utilisés comme des constantes nues de haut niveau, et Dayspeut être utilisé comme un type:

Days day = Saturday;   // Days.Saturday is an error

Et de même plus tard, pour tester:

if (day == Saturday)
    // ...

Ces enumvaleurs sont comme des constantes nues - elles n'ont pas de portée - avec un peu d'aide supplémentaire du compilateur: (à moins que vous n'utilisiez des classes d'énumération C ++ 11 ), elles ne sont pas encapsulées comme des membres d'objet ou de structure par exemple, et vous ne pouvez pas vous y référer en tant que membres de Days.

Vous aurez ce que vous cherchez avec C ++ 11 , qui introduit un enum class:

enum class Days
{
    SUNDAY,
    MONDAY,
    // ... etc.
}

// ...

if (day == Days::SUNDAY)
    // ...

Notez que ce C ++ est un peu différent de C de deux manières, l'une est que C nécessite l'utilisation du enummot - clé lors de la déclaration d'une variable:

// day declaration in C:
enum Days day = Saturday;
pb2q
la source
J'ai mis à jour la question, je pense que c'est maintenant plus clair ce que je veux exactement :) Au fait merci :)
Rika
14

Vous pouvez utiliser une astuce pour utiliser les étendues comme vous le souhaitez, déclarez simplement enum de la manière suivante:

struct Days 
{
   enum type
   {
      Saturday,Sunday,Tuesday,Wednesday,Thursday,Friday
   };
};

Days::type day = Days::Saturday;
if (day == Days::Saturday)
ataman1x
la source
9

Plutôt que d'utiliser un tas d'instructions if, les énumérations se prêtent bien aux instructions switch

J'utilise des combinaisons enum / switch dans le générateur de niveau que je construis pour mon jeu.

EDIT: Une autre chose, je vois que vous voulez une syntaxe similaire à;

if(day == Days.Saturday)
etc

Vous pouvez le faire en C ++:

if(day == Days::Saturday)
etc

Voici un exemple très simple:

EnumAppState.h

#ifndef ENUMAPPSTATE_H
#define ENUMAPPSTATE_H
enum eAppState
{
    STARTUP,
    EDIT,
    ZONECREATION,
    SHUTDOWN,
    NOCHANGE
};
#endif

Somefile.cpp

#include "EnumAppState.h"
eAppState state = eAppState::STARTUP;
switch(state)
{
case STARTUP:
    //Do stuff
    break;
case EDIT:
    //Do stuff
    break;
case ZONECREATION:
    //Do stuff
    break;
case SHUTDOWN:
    //Do stuff
    break;
case NOCHANGE:
    //Do stuff
    break;
}
Dean Knight
la source
La bonne chose ici est que les compilateurs vous diront si vous avez manqué de mettre un cas
dedans
Ne devez-vous pas utiliser l'énumération de classe dans ce cas?
Rika
1
enum est juste un type de données en C ++ Donc, déclarer une énumération comme je l'ai fait ci-dessus dans un fichier .h, puis inclure ce fichier dans le fichier .cpp dans lequel vous souhaitez l'utiliser vous donnera accès à l'énumération. Je viens de remarquer que j'ai oublié d'ajouter le #include dans mon exemple .cpp. Édition.
Dean Knight
De plus, je vois quelqu'un d'autre dire que les énumérations en C ++ sont globales. D'après mon expérience, en utilisant les énumérations comme ci-dessus, je ne peux y accéder que lorsque j'ai inclus le .h. Cela semble donc également bloquer l'accès mondial, ce qui est toujours bon. EDIT: Il semble que j'utilise sans le savoir des énumérations de manière C ++ 11 si je lis les choses correctement ...
Dean Knight
9

Si vous utilisez toujours C ++ 03 et souhaitez utiliser des énumérations, vous devez utiliser des énumérations dans un espace de noms. Par exemple:

namespace Daysofweek{
enum Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday};
}

Vous pouvez utiliser l'énumération en dehors de l'espace de noms comme,

Daysofweek::Days day = Daysofweek::Saturday;

if (day == Daysofweek::Saturday)
{
    std::cout<<"Ok its Saturday";
}
San
la source
8

Vous recherchez des énumérations fortement typées , une fonctionnalité disponible dans la norme C ++ 11 . Il transforme les énumérations en classes avec des valeurs de portée.

En utilisant votre propre exemple de code, c'est:

  enum class Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday};
  Days day = Days::Saturday;

  if (day == Days::Saturday)  {
    cout << " Today is Saturday !" << endl;
  }
  //int day2 = Days::Sunday; // Error! invalid

L'utilisation en ::tant qu'accesseurs aux énumérations échouera si vous ciblez une norme C ++ antérieure à C ++ 11. Mais certains anciens compilateurs ne le prennent pas en charge, ainsi certains IDE remplacent simplement cette option et définissent un ancien std C ++.

Si vous utilisez GCC, activez C + 11 avec -std = c ++ 11 ou -std = gnu11 .

Soyez heureux!

Alex Byrth
la source
1
Tu as oublié d'écrire enum class Days { ....
Martin Hennings
En effet. le réparer! Merci.
Alex Byrth
7

Cela ne devrait pas fonctionner en C ++:

Days.Saturday

Jours n'est pas une étendue ou un objet contenant des membres auxquels vous pouvez accéder avec l'opérateur point. Cette syntaxe n'est qu'un cismisme et n'est pas légale en C ++.

Microsoft a longtemps maintenu une extension C ++ qui vous permet d'accéder aux identifiants à l'aide de l'opérateur d'étendue:

enum E { A, B, C };

A;
E::B; // works with Microsoft's extension

Mais ce n'est pas standard avant C ++ 11. En C ++ 03, les identifiants déclarés dans une énumération n'existent que dans la même portée que le type énumération lui-même.

A;
E::B; // error in C++03

C ++ 11 rend légal la qualification des identificateurs d'énumération avec le nom d'énumération et introduit également des classes d'énumération, qui créent une nouvelle étendue pour les identificateurs au lieu de les placer dans l'étendue environnante.

A;
E::B; // legal in C++11

enum class F { A, B, C };

A; // error
F::B;
bames53
la source
4

Malheureusement, les éléments de l'énumération sont «mondiaux». Vous y accédez en faisant day = Saturday. Cela signifie que vous ne pouvez pas avoir enum A { a, b } ;et enum B { b, a } ;car ils sont en conflit.

Grzegorz
la source
2
Jusqu'à ce que vous utilisiez enum classen C ++ 11, c'est. Avant cela, vous devez faire des cours factices.
chris
Je ne sais pas C ++ 11. Je suppose que la question fait référence à C ++. Oui, l'utilisation de classes ou d'espaces de noms fera l'affaire.
Grzegorz
@Grzegorz: je pense que chris fait référence à la classe enum nouvellement introduite qui fournit des énumérations fortement typées.
Rika
@Hossein: Merci de l'avoir signalé. J'ai trouvé des explications sur la classe num, et je sais de quoi Chris parlait. Merci beaucoup.
Grzegorz
@Grzegorz: Je ne voulais pas manquer de respect, j'ai juste pensé que je pourrais aider, désolé pour tout malentendu probable.Je vous remercie encore pour votre temps et pour m'aider;)
Rika
4

Alors que C ++ (à l'exception de C ++ 11) a des énumérations, les valeurs qu'elles contiennent sont "divulguées" dans l'espace de noms global.
Si vous ne voulez pas les faire fuir (et que vous N'AVEZ PAS BESOIN d'utiliser le type enum), tenez compte des points suivants:

class EnumName {  
   public:   
      static int EnumVal1;  
      (more definitions)  
};  
EnumName::EnumVal1 = {value};  
if ([your value] == EnumName::EnumVal1)  ...
InitializeSahib
la source
3

Les énumérations en C ++ sont comme des entiers masqués par les noms que vous leur donnez, lorsque vous déclarez vos valeurs d'énumération (ce n'est pas une définition seulement un indice sur la façon dont cela fonctionne).

Mais il y a deux erreurs dans votre code:

  1. Épeler enum tous les minuscules
  2. Vous n'avez pas besoin du Days. avant samedi.
  3. Si cette énumération est déclarée dans une classe, utilisez if (day == YourClass::Saturday){}
Balázs Édes
la source
L'OP a changé l'orthographe / cas 16 minutes après le post initial ( révision 1 à révision 2 ).
Peter Mortensen
1

Je pense que votre problème racine est l'utilisation de .au lieu de ::, qui utilisera l'espace de noms.

Essayer:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days::Saturday;
if(Days::Saturday == day)  // I like literals before variables :)
{
    std::cout<<"Ok its Saturday";
}
James Oravec
la source
Cela ne fonctionne pas: pour utiliser l' Days::étendue comme dans votre exemple, vous devez définir l'énumération avec enum class Dayset utiliser l'extension Microsoft C ++ 03 + ou C ++ 11.
Futal
@Futal, ce qui précède a fonctionné avec Borland C ++ Builder. La saveur / version de C ++ n'est pas en cause.
James Oravec
1
votre version de Borland C ++ Builder doit utiliser C ++ 11 ou une version plus récente. Gcc et Clang donnent tous deux des erreurs ou des avertissements si votre exemple est compilé avec -std=c++98ou -std=c++03. Clang est tout à fait clair: warning: use of enumeration in a nested name specifier is a C++11 extension.
Futal
1

Si nous voulons la sécurité de type stricte et l'énumération de portée, l'utilisation enum classest bonne en C ++ 11.

Si nous devions travailler en C ++ 98, nous pouvons utiliser les conseils donnés par InitializeSahib, Sanpour activer l'énumération de portée.

Si nous voulons également la sécurité de type stricte, le code suivant peut implémenter quelque chose comme enum.

#include <iostream>
class Color
{
public:
    static Color RED()
    {
        return Color(0);
    }
    static Color BLUE()
    {
        return Color(1);
    }
    bool operator==(const Color &rhs) const
    {
        return this->value == rhs.value;
    }
    bool operator!=(const Color &rhs) const
    {
        return !(*this == rhs);
    }

private:
    explicit Color(int value_) : value(value_) {}
    int value;
};

int main()
{
    Color color = Color::RED();
    if (color == Color::RED())
    {
        std::cout << "red" << std::endl;
    }
    return 0;
}

Le code est modifié à partir de l'exemple de classe Month dans le livre Effective C ++ 3rd: Item 18

Xu Hui
la source
-15

Tout d'abord, faites «E» en énumération, «e» en minuscule.

Ensuite, déposez le nom du type «Days» dans «Days.Saturday».

Troisièmement ... achetez-vous un bon livre C ++.

vikramjitSingh
la source
5
Désolé d'avoir obtenu tous ces votes négatifs (je veux dire, la réponse le mérite bien), mais cela ne signifie pas que vous devez quitter la communauté pendant 6 ans. Revenez nous rejoindre. Vous avez aussi quelque chose à apporter. Soit utile. Partager le savoir.
Gabriel Staples