IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Énumérations fortement typées C++0x


Sujet :

Langage C++

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut Énumérations fortement typées C++0x
    Bonjour,

    Je met une partie de mon code à jour pour C++0x, et là je m'occupe des enums. Seulement comme avant elles pouvaient se convertir implicitement en entier je voulais savoir si le comportement du code ci-dessous est certifié par le nouveau standard :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    enum class MyEnum { A, B, C };
     
    int nb = static_cast<int>(MyEnum::B); // nb vaut 1
    A savoir qu'ici nb vaudrait bien 1 donc que si on cast A vaut 0, B vaut 1 et C vaut 2.
    En effet je m'en servais pour faire des accès à des std::vector.

    Merci à tous!

  2. #2
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    Je viens de voir qu'il était possible de permettre le cast implicite avec le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    enum class MyEnum : unsigned int
    {
        A,
        B
    };
    J'aurais aimé savoir deux choses :
    - 1: Est-il judicieux de rajouter ce ": unsigned int" ? Ou vaut-il mieux un cast bien explicigte ou il faut?

    - 2: Le standard confirme-t-il que si l'on ne dit rien, la première valeur vaut zéro? Et les suivantes sont incrémenter par pas de 1?

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par victor_gasgas Voir le message
    Je viens de voir qu'il était possible de permettre le cast implicite avec le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    enum class MyEnum : unsigned int
    {
        A,
        B
    };
    J'aurais aimé savoir deux choses :
    - 1: Est-il judicieux de rajouter ce ": unsigned int" ? Ou vaut-il mieux un cast bien explicigte ou il faut?
    Je me pose la question...

    Je ne me suis pas vraiment intéressé aux énum classes de C++11, ni à ce qui a mené à les envisager, mais je me dis que si elles ont été envisagées, et pour atteindre deux objectifs :

    1- qu'un meme identifiant de valeur énumérée puisse se retrouver dans deux énumération distinctes, le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    enum myFirstEnum
    {
        none,
        /* ... */
        count
    };
    enum mySecondEnum
    {
        none,
        /* ... */
        count
    };
    étant illégal (none et count représentent la valeur de myFirstEnum ou de MySecondEnum ) en C++03, mais pouvant devenir légal dans le cas des classes enum (en C++11):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    enum class MyFirstEnum
    {
        none,
        /* ... */
        count
    };
    enum class MySecondEnum
    {
        none,
        /* ... */
        count
    };
    /* tout à fait légal : MyFirstEnum::none != MySecondEnum::none */
    -2 pour s'assurer que l'utilisateur est clairement conscient de passer une valeur énumérée.

    Le fait d'indiquer que tes valeurs énumérées sont sensées être d'un type particulier peut avoir un intérêt dans le sens où tu fixe définitivement la taille que ces valeurs peuvent prendre en mémoire (parce que, même si ce n'est pas forcément le cas en pratique, le compilateur est libre d'utiliser le "plus petit type primitif capable de représenter l'ensemble des valeurs énumérées" ).

    Par contre, je me dis que, ce faisant, tu contourne justement le cas intéressant obligeant l'utilisateur à être conscient qu'il utilise une énumération

    Cela ne me semble pas "illégal", mais chacun se fera son opinion (tu auras compris que je penche plutôt du coté des "plutot pas le faire" )
    - 2: Le standard confirme-t-il que si l'on ne dit rien, la première valeur vaut zéro? Et les suivantes sont incrémenter par pas de 1?
    ca, c'est déjà garanti en C++03 :
    Citation Envoyé par la norme 7.2 Enumeration declarations [dcl.enum]
    1.The identifiers in an enumerator-list are declared as constants, and can appear wherever constants are required. An enumerator-definition with = gives the associated enumerator the value indicated by the constant-expression. The constant-expression shall be of integral or enumeration type. If the first enumerator has no initializer, the value of the corresponding constant is zero. An enumerator-definition without an initializer gives the enumerator the value obtained by increasing the value of the previous enumerator by one.
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    Merci! Je pense que je vais suivre le fait du static_cast bien explicite qui me paraît bien plus judicieux, qui est aussi ton avis.

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    (extrait d'un vieil article commencé là dessus, certaines choses ont pu évoluées, je n'ai pas fait le check) :
    En C++03
    Les énumérations sont déjà des types en tant que tels apportant les garanties du typage :
    Code De quelle couleur est une orange ? : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    enum fruit{appel, pear, watermelon, orange};
    enum color{red, black, green};
     
    int main()
    {
       fruit f;
       f = red; // erreur
       color c;
       c = orange; // erreur
       return 0;
    }

    Cependant, ce sont des types implicitement convertibles vers des valeurs entières. Cette conversion permet de comparer des choux et des carottes :
    Code Les lapins crétins : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    #include <iostream>
    enum carotte{nantaise_amelioree, touchon, demi_longue_chantenay};
    enum choux{cabu, bruxelle, fleur};
     
    int main()
    {
       carotte ca(nantaise_amelioree);
       choux cx(cabu);
       if(ca==cx)
       {
          std::cout<<"A rendre cretin un lapin !\n";
       }
     
       return 0;
    }
    La conversion dans l'autre sens complique aussi l'apprentissage :
    Code Conversion asymétrique : : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
     
    enum E {e_1, e_2, e_3};
    E e(e_1);
    int i = e; // OK
    e = i; // Erreur !
    Les noms des valeurs d'une énumération ont la portée de leur type et ne sont pas encapsulés par celui-ci. Il n'est dès lors pas possible d'avoir deux valeurs d'énumération avec le même nom :
    Code Un nom encombrant : : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    enum color = {green, yellow, orange};
    enum frut = {apple, strawberry, orange}; // Erreur : orange déjà défini dans color

    Enfin, le type sous-jascent d'une énumération est laissé à la discrétion du compilateur, la seule contrainte étant qu'il doit s'agir d'un type entier suffisamment grand pour contenir les valeurs et sans qu'il dépasse un int ... sauf si nécessaire pour une valeur. Ce type peut alors varier selon les compilateurs :
    Code Quelle taille pour un enum ? : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
     
    #include <iostream>
    enum answer {no, yes, maybe};
     
    int main()
    {
       std::cout<<sizeof(answer)<<"\n"; // dépendant du compilateur !
       return 0;
    }

    Toutes ces contraintes peuvent actuellement se contourner mais au détriment de la facilité qu'est supposé apporter le type enum. Les énumérations font donc peau neuve pour palier simplement à ces problèmes.

    en C++11
    C++0x introduit une nouvelle catégorie d'énumération : les classes d'énumération. La déclaration nécessite l'ajout du mot-clé class (ou struct) :
    Code Les classes d'énumérations : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    enum struct my_enum { val_1, val_2, val_3};
    enum class another_enum {another_1, another_2,another_3};

    class et struct sont strictement équivalents et ont la même sémantique. Je présume que les deux mot-clés ont été ajoutés pour garder une cohérence avec le reste du langage où ces deux mots se trouvent. Cependant, il s'agit bien d'énumération et non de classe ou de structure. On ne peut pas hériter d'une classe d'énumération.
    La conversion vers un type entier n'est plus immédiate :
    Code Une classe d'énumération n'est pas une valeur entier : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    enum struct digit { zero, one, two, three, four, five, six, seven, height, nine};
    int main()
    {
       digit d = 1;// erreur
       int i = d; // erreur
       if(d) // erreur
       {//...
       }
       return 0;
    }

    Les noms des valeurs doivent maintenant être préfixés par la classe à laquelle ils appartiennent.
    Code Des valeurs bien séparées : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    enum struct color {green, yellow, orange};
    enum struct frut {apple, strawberry, orange}; // OK !
     
    int main()
    {
       color c1 = orange; // erreur
       color c2 = color::orange; // OK
       frut f1 = frut::orange; // OK
       frut f2 = color::orange; // erreur : plus de confusion possible
       return 0;
    }

    Les choux et les carottes ne peuvent plus être comparés :
    Code Les choux ne sont plus des carottes : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    enum class carotte{nantaise_amelioree, touchon, demi_longue_chantenay};
    enum class choux{cabu, bruxelle, fleur};
     
    int main()
    {
       carotte ca(carotte::nantaise_amelioree);
       choux cx(choux::cabu);
       if(ca==cx)// erreur
       {
       }
     
       return 0;
    }

    Le type sous-jascent d'une énumération peut être précisé par le développeur aussi bien pour les nouvelles classes d'énumération que pour les anciens enum :
    Code Indiquer le type sous-jascent : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    enum answer : bool {yes, no}; // occupe la même place qu'un bool
    enum struct relation  : unsigned char {less, equal, greater}; // occupe la même place qu'un unsigned char
    enum class level : int {low, medium, high}; // occupe la même place qu'un int
    A noter que cela ne définit pas une conversion implicite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    type sous-jascent ne veut pas dire type équivalent
     
    enum struct relation  : unsigned char {less, equal, greater};
    enum class level : int {low, medium, high};
     
    int main()
    {
       unsigned char uc(0);
       relation r = uc; // erreur
       level le(level::low);
       int i = le;// erreur
     
       return 0;
    }
    En précisant le type sous-jascent d'une énumération, le compilateur a toutes les informations nécessaires et suffisantes pour permettre la déclaration d'une énumération indépendamment de sa définition :
    Code Déclaration anticipée des énumérations : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    enum color : unsigned short;
    color get_red();
    void dump(color);
     
    int main()
    {
       color c;
       c = get_red();
       dump(c);
       return 0;
    }
     
     
    enum color : unsigned short {red, green, blue};
     
    color get_red()
    {
       return color::red;
    }
     
    #include <iostream>
    void dump(color c_)
    {
       switch(c_){
          case color::red:
             std::cout<<"red\n";
             break;
          case color::green:
             std::cout<<"green\n";
             break;
          case color::blue:
             std::cout<<"blue\n";
             break;
       }
    }
    La définition des valeurs effectives d'une énumération peuvent alors être masquée à ceux qui n'en n'ont pas besoin. Un fichier d'en-tête peut contenir la déclaration anticipée de l'énumération et les fonctions de manipulation nécessaires. Un fichier de définition précise les valeurs effectives de l'énumération et implémente les fonctions déclarées.

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Par défaut
    Merci beaucoup pour ce rappel!

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 03/05/2011, 10h20
  2. Vues partielles fortement typées
    Par tresorunikin dans le forum Zend Framework
    Réponses: 1
    Dernier message: 14/04/2011, 18h44
  3. Réponses: 3
    Dernier message: 08/03/2010, 11h25
  4. Sous énumération ou type similaire
    Par Neitsa dans le forum C#
    Réponses: 1
    Dernier message: 14/04/2008, 17h40
  5. dataset fortement type
    Par bicho dans le forum VB.NET
    Réponses: 2
    Dernier message: 16/03/2007, 13h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo