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

 C++ Discussion :

énumérations - quelles sont leur limites [enum] [enumération]


Sujet :

C++

  1. #1
    Membre expert

    Profil pro
    activité : oui
    Inscrit en
    Janvier 2014
    Messages
    1 262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : activité : oui

    Informations forums :
    Inscription : Janvier 2014
    Messages : 1 262
    Points : 3 409
    Points
    3 409
    Par défaut énumérations - quelles sont leur limites [enum] [enumération]
    bonjour,
    soyez indulgent sur ma présentation, c'est mon 2e post (je suis ouvert à tout conseils)


    Je suis sous windows8 et suis actuellement face à l'impossibilité d'installer ne serait-ce qu'un IDE comme "eclipse".
    "JRE requis" >> le support windows expliquant qu'il est installé nativement sur ttes ces plateform mais pas activé ou qqch du genre...
    Bref, je vous passe les détails.


    J'utilise alors des compilateurs en ligne:
    - Clang (3 & 3.2)
    - GCC (4.3.4, 4.5.1, 4.5.3, 4.6.3, 4.7, 4.7.2 & 4.8 prerelease)
    - Intel ICC 13.0.1
    - Microsoft (VC++ alpha CTP, Nov 2012)
    supportant le C++4.3.2, 4.8.1, C++11

    J'ai parcouru plusieurs cours C++ sur les fondamentaux:

    int, short int, unsigned int, long int, unsigned long **// entiers ***********// intégral ******// arithmétiques //fondamentaux
    enum ******// énuméraion ******************// entiers ***********//************//***********//
    char, signed char, unsigned char, wchar_t ********// caractères *********// ***********//***********//
    bool ************************************// booléen ***********// ***********//***********//
    float, double, long double ********************// réels **************************// ***********//
    void ******// uniquement pour les valeur de retour et les types de pointeurs **************************//
    // le reste ****************************************************************************// dérivés


    Ce que j'ai retenu concernant les énumérations, c'est qu'elle est un type de la catégorie des entiers, représentent une liste de constantes de type entier (cet ensemble sera du type définit par enum), et peut optionnellement être nommé, que les valeur par défaut sont les indices d'un tableau, et peuvent être initialisé, qu'une variable de type enum, n'est par définition pas constante, mais bel et bien une variable. Et pour finir, les énumérateurs (composants de la liste) ne sont pas accessible comme des membres (classe.membre ou classe->membre), mais uniquement comme de simples constantes.
    FAUX: une énumération est aujourd'hui accessible
    Citation Envoyé par koala01
    C++11 est arrivé avec la notion d'énumération fortement typée (enum class qui lève un certain nombre de restrictions propres aux énumérations "classiques":
    1. Les valeurs énumérées doivent être "pleinement qualifiées" (comprends: sous la forme de MyEnum::laValeur) ce qui permet à deux énumérations distinctes d'avoir une valeur énumérée portant le même nom : comme le nom de l'énumération intervient dans la résolution du nom de la valeur énumérée, il n'y a plus de conflit entre les deux valeurs énumérées
    1. Ces énumérations peuvent être déclarées par anticipation comme n'importe quelle structure, union ou classe
    1. ...


    D'après les sous-ensembles que j'ai indiqué plus haut, j'en déduit que les énumérations imbriqués sont possible.
    ...et bien non

    Néanmoins, je me trouve face à plusieurs interrogations:
    - est-ce une seconde syntaxe valide de déclaration ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    enum leType {PARAMETRE1(a), /*...*/};   // à priori invalide (contrairement aux déclaration: "int nombre(451);" )
    //au lieu de:
    enum leType {PARAMETRE1=a, /*...*/};
     
    //ou encore
    enum leType {PARAMETRE1=a, /*...*/} laVariable;   // à priori validde (comme une structure)
    //au lieu de:
    enum leType {PARAMETRE1=a, /*...*/};
    leType laVariable;
    - les constantes d'une même liste sont-elles toute de type identique ?
    Non, elles peuvent être de types différents, contrairement à leurs noms (où c'est une obligation), mais se doivent d'être de type entier (ne pas confondre avec intégral, une catégorie plus large) et ne peut être de type énumération également. Elle peuvent tout de même être de type booléennes ou caractères, mais seront converti sous forme d'entier numérique à la compilation.
    - est-ce un type entier en particulier, ou le compilateur le détermine suivant le contexte ?
    C'est le compilateur qui détermine le type de chaque constantes suivant le contexte, de même que pour le type de l'énumération: il choisit un type capable de stocker le plus grand. Attention, ce type est alors un type unique rattaché au nom de l'énumération, et cette dernière est typé.
    - d'après mes essais en ligne, une variable de type enum peut prendre des valeurs non contenu dans sa liste.
    ****dans ce cas, quelle est l'intérêt par rapport à une déclaration simple avec const ?
    ****(j'ai pourtant cru comprendre que ce problème arrivait en C, mais pas en C++)
    Une variable de type énumération ne peut pas prendre de valeur autre que celle de sa liste, ds le cas contraire, le compilateur est défaillant.


    complément tiré des réponses ci-dessous:
    - Les énumérations apportent une sécurité supplémentaire:
    Seul des valeurs numériques entières seront utilisé par l'énumération. Ces valeurs sont rattaché à un type propre. Une variable de ce type ne peut prendre d'autres valeurs que celles des énumérateurs.

  2. #2
    Membre chevronné
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    855
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 855
    Points : 2 176
    Points
    2 176
    Par défaut
    Bonjour a toi !

    Pour ton probleme d'installation d'IDE lie a java (comme eclipse notamment), va sur cette page. As-tu essaye d'installer codeblocks par-exemple ?

    En fait les enums fonctionnent un peu comme des defines (pour simplifier) mais avec l'avantage que chaque element vaudra la valeur du precedent + 1. Tu peux tres bien faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    enum {
      e_A = 12,
      e_B,
      e_C
    };
     
    int x = e_A;
    Cela dit, si tu veux a tout prix specifier que c'est bien un enum que ta variable / fonction / methode attend, tu peux donner un nom a ton enum :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    enum MyEnum
    {
      e_A,
      e_B,
      e_C
    };
     
    MyEnum x = e_A;
    void func(MyEnum x)
    {
      int a = x;
    }
    Citation Envoyé par Steinvikel
    - les constantes d'une même liste sont-elles toute de type identique ?
    Teste ce code et tu verras, l'erreur donnee par le compilateur me semble assez explicite :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    enum
    {
      e_A = 0.4f, //float
      e_B = "salut",
      e_C = 0
    };
    Citation Envoyé par Steinvikel
    - d'après mes essais en ligne, une variable de type enum peut prendre des valeurs non contenu dans sa liste.
    ****dans ce cas, quelle est l'intérêt par rapport à une déclaration simple avec const ?
    Comme je l'ai montre dans mon exemple un peu plus haut, un enum n'est en gros qu'un typedef sur un "constant integer" (mais normalement tu le sais deja car tu as vu ce que te disait le compilateur tout a l'heure !). L'avantage de cette methode c'est de te fournir la possibilite d'initialiser des variables constantes sans avoir a retaper la valeur de chacune si elles se suivent. Exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    #define e_A 0
    #define e_B 1
    #define e_C 2
     
    enum
    {
      e_A,
      e_B,
      e_C
    };

  3. #3
    Membre expert

    Profil pro
    activité : oui
    Inscrit en
    Janvier 2014
    Messages
    1 262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : activité : oui

    Informations forums :
    Inscription : Janvier 2014
    Messages : 1 262
    Points : 3 409
    Points
    3 409
    Par défaut
    Le problème concernant JAVA ne vient pas de JAVA, mais bien de Windows, ils en parles sur leur support, mais n'abordent pas les explications pour y résoudre.

    Je connais le fonctionnement global de enum, mais ces détails reste un mystère pour l'instant.

    j'ai testé ton code sur plusieur compilateur, il me parle effectivement de "integer constant".
    ("prog.cpp:8:9: error: enumerator value for ‘e_A’ is not an integer constant")
    Mais ne connaissant pa le jargon anglais, je ne suis pas sûr de la bonne interprétation du message d'erreur. "integer" designe la catégorie des entiers ou bien int ? chacun des énumérateurs est attribué du même type ?

    L'avantage de cette méthode c'est de te fournir la possibilité d'initialiser des variables constantes sans avoir à retaper la valeur de chacune si elles se suivent.
    Une simple constante symbolique (#define) fait la même chose non ?

    Je ne comprend pas ton exemple, je crois que tu as omis quelque-chose, les énumérateurs doivent être des constantes symboliques.
    j'ai tout de même tenté de compiler, une erreur survient à propos de "primary expression"

  4. #4
    Membre chevronné
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    855
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 855
    Points : 2 176
    Points
    2 176
    Par défaut
    Lequel as-tu tente de compiler ? Si c'est celui-la :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #define e_A 0
    #define e_B 1
    #define e_C 2
     
    enum
    {
      e_A,
      e_B,
      e_C
    };
    C'est normal qu'il n'ait pas compile, je voulais juste te montrer l'equivalent avec les define et pourquoi un enum dans ce cas etait plus indique (pour eviter les redondances notamment).

    j'ai testé ton code sur plusieur compilateur, il me parle effectivement de "integer constant".
    ("prog.cpp:8:9: error: enumerator value for ‘e_A’ is not an integer constant")
    Mais ne connaissant pa le jargon anglais, je ne suis pas sûr de la bonne interprétation du message d'erreur. "integer" designe la catégorie des entiers ou bien int ? chacun des énumérateurs est attribué du même type ?
    En gros c'est ca, un integer constant pourrait se traduire par :

    Ce qui signifie donc que tu ne peux pas mettre d'autres valeurs que des int dans un enum (a l'inverse d'un define qui lui peut tres bien accepter tout type de valeur). Donc un enum sera toujours de type int.

    Une simple constante symbolique (#define) fait la même chose non ?
    Oui et non. Tu peux tres bien faire ceci avec un define :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #define M_PI 3.14
    #define BONJOUR "Bonjour !"
    #define MY_ABS(x) ((x) > 0 ? (x) : -(x))
    Mais pas dans un enum :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    enum
    {
      M_PI = 3.14,
      BONJOUR = "Bonjour !"
    };
    Les defines sont legerement plus complexe. Je t'invite a faire un tour sur ce lien sur tu veux en savoir plus.

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Salut,
    Citation Envoyé par Steinvikel Voir le message
    Une simple constante symbolique (#define) fait la même chose non ?
    Dis plutôt
    Une simple constante symbolique (#define) fait presque la même chose non ?
    Apprête toi à être surpris de constater à quel point ce simple mot de sept lettres changent énormément de chose

    La principale, c'est que la directive préprocesseur ne fait que remplacer le symbole par la valeur équivalente, sans se poser la moindre question.

    C'est à dire que si tu définis deux symboles, par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    #define MYSTRING "azerty"
    #define MYINT 3641
    il remplacera, partout où ces deux symboles sont connus, MYSTRING par azerty et MYINT par 3641 sans s'inquiéter de savoir si cela a du sens de le faire.

    Autrement dit, un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::string str(MYSTRING); 
    int value = MYINT;
    sera tout à fait correct, parce que MYSTRING correspond à un pointeur sur une chaine de caractères C-style (constante) et qu'il existe bel et bien un constructeur adapté pour en créer une chaine de caractère "C++", et MYINT est, bel et bien, une valeur de type entier qui peut donc être affectée à un autre entier. Jusque là, tout va pour le mieux dans le meilleur des mondes

    Le problème, c'est que le préprocesseur fera aussi la substitution des valeurs pour le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    std::string str(MYINT);
    int value = MYSTRING;
    Et le pire, c'est que même le compilateur n'y trouveras sans doute rien à redire (bon, allez, il émettra sans doute un avertissement du type "utilisation d'une valeur non signée pour affectation à une valeur signée" en ce qui concerne value, mais c'est bien tout ce qu'il fera).

    Pourquoi acceptera-t-il ce code, peux-tu te demander

    Hé bien, simplement parce qu'un pointeur n'est jamais qu'une variable numérique (généralement non signée) qui correspond à l'adresse mémoire à laquelle se trouve l'élément du type indiqué. et le fait est que
    (ce qui est, en gros, ce par quoi MYSTRING sera substitué) déclare str comme un pointeur sur le premier élément du tableau de caractères qui contient les éléments contigus 'a', 'z', 'e', 'r', 't', 'y' et '\0'.

    Comme c'est un pointeur, c'est une variable numérique entière, et comme il est possible de convertir implicitement n'importe quel type entier en un autre type entier (au pire, en "perdant" certaines informations ou en transformant une valeur exclusivement positive en une valeur potentiellement négative), le compilateur ne trouvera strictement rien à redire au code .

    De l'autre coté, il n'y a strictement aucune raison pour que le compilateur considère que la valeur 3641 représente une adresse invalide. Après tout, c'est bel et bien une valeur entière, et elle est bel et bien non signée (note d'ailleurs que, fusse-t-elle signé (-6548), cela n'aurait rien changé : elle aurait été convertie en valeur non signée ).

    Le problème, c'est que cette adresse (3641) peut contenir strictment n'importe quoi! Mais tu peux être quasiment persuadé qu'il ne s'agira pas d'une adresse à laquelle tu trouveras un chaine de caractères utile à ton application

    A partir de là, tout peut arriver. La std::string peut contenir une chaine correspondra à tous les caractères (visibles, supérieurs ou égal à 32 comme non visibles, inférieurs à 32) rencontrés avant ce qui pourrait passer pour un caractère '\0'... Cela risque de faire beaucoup et de provoquer un affichage... pour le moins exotique

    Les énumérations apportent une garantie supplémentaires : celle que nous aurons affaire, quoi qu'il arrive, à une (ou plutôt à un ensemble de) valeur(s) numérique(s) entière(s). Et mieux encore, elle indique que les valeurs énumérées font partie d'un groupe commun, qu'elles représentent des valeurs qui "vont bien ensembles".

    Le type d'une valeur énumérée n'est donc pas "n'importe quel type de valeur numérique entière", mais bien "le type bien particulier qui correspond à l'énumération (au groupe de valeur énumérées) dans lequel une valeur énumérée et définie".

    Cela permet au compilateur d'effectuer des vérifications qu'il est tout à fait incapable d'effectuer lorsqu'on a recours aux autres techniques (en fait, il y a une troisième technique dont je ne parlerai pas ici ), que l'on peut --entre autres -- mettre à profit dans les tests à choix multiples (switch... case) :
    Il peut s'assurer que la valeur énumérée fait bel et bien partie du groupe adéquat.
    Par exemple, si tu as une énumérations, proches de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    enum Enum1{
       zero,
       one, 
       two
       /* ... */
    };
    et que tu as une valeur FIRST (quelle que soit la manière dont elle est définie), le code suivant s'arrêtera sur une erreur au motif que FIRST ne fait pas partie de l'énumération de type Enum1:
    Code : 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
    Enum1 value;
    /* on calcule value à peu près comme on veut ;) */
    switch (value){
        case  zero : // ca foctionne : zero est une valeur énumérée de type Enum1
            /*... */
           break;
        case  one : // ca foctionne : one est une valeur énumérée de type Enum1
            /*... */
           break;
        case  two : // ca foctionne : two est une valeur énumérée de type Enum1
            /*... */
           break;
        case  FIRST : // ca ne compile pas : FIRST n'est pas une valeur énumérée valide
            /*... */
           break;
    }
    La deuxième vérification que le compilateur peut effectuer consiste à s'assurer que toutes les valeurs énumérées sont bien prises en compte :

    Le code suivant provoquera un avertissement proche de "vous n'avez pas prévu d'instructions pour la valeur zero"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Enum1 value;
    /* on calcule value à peu près comme on veut ;) */
    switch (value){
        case  one : // ca foctionne : one est une valeur énumérée de type Enum1
            /*... */
           break;
        case  two : // ca foctionne : two est une valeur énumérée de type Enum1
            /*... */
           break;
    }
    Du point de vue de la sécurisation du code en général, il n'y a pas photo entre l'utilisation d'une directive #define et celle d'une énumération
    Ce que j'ai retenu concernant les énumérations, c'est qu'elle est un type de la catégorie des intégraux, représentent une liste de constantes de type entier (cet ensemble sera de type enum), et peut optionnellement être nomé, que les valeur par défaut sont les indices d'un tableau, et peuvent être initialisé, qu'une variable de type enum, n'est par définition pas constante, mais bel et bien une variable. Et pour finir, les énumérateurs (composants de la liste) ne sont pas accessible comme des membres (classe.membre ou classe->membre), mais uniquement comme de simples constantes.
    Il y a beaucoup à dire sur cette phrase, je vais reprendre les différents aspects un à un si tu veux bien :
    Ce que j'ai retenu concernant les énumérations, c'est qu'elle est un type de la catégorie des intégraux, représentent une liste de constantes de type entier (cet ensemble sera de type enum),
    Ca, c'est juste.
    et peut optionnellement être nomé
    Je dirais plutôt le contraire : qui est généralement nommé, sauf cas particulier. Une énumération peut effectivement être anonyme, mais l'utilisation d'énumérations anonymes est une exception
    que les valeur par défaut sont les indices d'un tableau,
    C'est vrai pour les valeurs par défaut, mais c'est très loin d'être le cas de manière systématique.

    Par exemple: j'ai récemment écrit une énumération qui prenait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class MyEnum{
        dirUnset = 65,
        front = 8,
        back = -8,
        left = -1,
        right = 1,
        frontLeft = 7,
        backLeft = -9,
        frontRight = 9,
        backRight = -7
    }
    Ces valeur énumérées permettent de définir une case adjacente sur base d'une direction dans tableau qui serait composé de 8*8 cases (comme un jeu d'échecs )

    Les énumérations sont aussi souvent utilisées pour définir des "flags", des valeurs qui sont représentables en ne mettant qu'un seul bit à 1 et qui peuvent être associées de de manière non exhaustive (comprends : pour lesquelles toutes les combinaisons possibles donneront une valeur différente )
    qu'une variable de type enum, n'est par définition pas constante, mais bel et bien une variable.
    En fait, c'est toute la distinction entre l'énumération en elle-même, qui est un type (que l'on peut donc utiliser pour déclarer une variable) et les valeurs énumérées qu'elles contient. Les valeurs énumérées sont ce que l'on appelle des constantes de compilation : Le compilateur sait, dans l'exemple que j'ai donné, parfaitement que frontRight est équivalent à 9, ce qui fait qu'il peut parfaitement faire des calculs (frontRigh - left, par exemple ) au moment de la compilation pour définir une autre constante de compilation. Cet aspect est parfois particulièrement intéressant
    Et pour finir, les énumérateurs (composants de la liste) ne sont pas accessible comme des membres (classe.membre ou classe->membre), mais uniquement comme de simples constantes.
    Jusqu'à l'arrivée de C++11, c'était bien le cas.

    C++11 est arrivé avec la notion d'énumération fortement typée (enum class qui lève un certain nombre de restrictions propres aux énumérations "classiques":
    1. Les valeurs énumérées doivent être "pleinement qualifiées" (comprends: sous la forme de MyEnum::laValeur) ce qui permet à deux énumérations distinctes d'avoir une valeur énumérée portant le même nom : comme le nom de l'énumération intervient dans la résolution du nom de la valeur énumérée, il n'y a plus de conflit entre les deux valeurs énumérées
    2. Ces énumérations peuvent être déclarées par anticipation comme n'importe quelle structure, union ou classe
    3. ...

  6. #6
    Membre expert

    Profil pro
    activité : oui
    Inscrit en
    Janvier 2014
    Messages
    1 262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : activité : oui

    Informations forums :
    Inscription : Janvier 2014
    Messages : 1 262
    Points : 3 409
    Points
    3 409
    Par défaut
    Pour appuyer nos exemples et expications, je vous propose un exemple des plus simple, qui je pense permettra d'augmenter la clarté de ce qui semble devenir un post bien fournit.
    Code : 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
    /*   attention, ce code ne compile pas   */
    #include <iostream>
    using namespace std;
     
    int main()
    {
    	enum couleur {BLEU, BLANC, ROUGE, VERT=3, TEST1='A', TEST2};
    	couleur pot;
    	char lettre= 'A';
     
    	cout<< "couleur disponibles: bleu, blanc, rouge, vert, test1 et test2"<< endl;
    	cout<<pot<<'-'<< BLEU<<' '<< BLANC<<' '<< ROUGE<<' '<< VERT<< '-'<< TEST1<<' '<< TEST2<< endl;
    	cout<< lettre<< endl;
     
    	pot= VERT;
    	cout<<pot<< endl;
     
    	pot= 3; // ligne à supprimer pour avoir un code qui fonctionne
    	cout<<pot<< endl;
    }
    msg err:
    prog.cpp:15:5: error: invalid conversion from ‘int’ to ‘main()::couleur’ [-fpermissive]
    pot= 3; // valeur inexistante dans l'énumération

    le résultat attendu (affiché sur la console):
    couleur disponibles: bleu, blanc, rouge, vert, test1 et test2
    0-0 1 2 3-A B //65 66 à la place de A & B en réalité
    A
    3
    3 // n'est pas permis

    Koala01: Le type d'une valeur énumérée n'est donc pas "n'importe quel type de valeur numérique entière", mais bien "le type bien particulier qui correspond à l'énumération (au groupe de valeur énumérées) dans lequel une valeur énumérée et définie".
    j'ai peur de ne plus te suivre... une énumération nommé "couleur" est un type en soit, (presque) comme si j'avais utilisé typedef... ce que tu appel "valeur énuméré", je n'en suis pas sûr, est l'une des constantes entre accolade.
    Je doit donc comprendre que ce qui se trouve dans l'énumération doit être de type intégraux (apparemment) et que l'usage de la variable "pot" qui est de type "couleur" n'accepte uniquement que des valeur de type "couleur" et aucun autre, qu'il soit fondamental ou dérivé.
    (ce qui expliquerait pourquoi "pot= 3;" ne compile pas)

    Imperio, tu m'a préciser que, en gros, un élément d'énumération est un const int... dans mon exemple, un char est toléré à la compilation, y a-t-il promotion/conversion ?.. car je remarque qu'un caractère est utilisé dans l'enum mais qu'il n'est pas affiché.
    PS: oui, c'était bien celui là que j'avais compilé

    koala01,
    1) Si j'ai bien compris (dans un autre sujet) une variable typé par enum est représenté par un type qui peut stocké n'importe lequel de sa liste, mais est considéré comme un type unique... "couleur" dans notre exemple.
    Cela pourait aussi bien être un unsigned short int comme un long long, ou un char.

    2)
    Les valeurs énumérées sont ce que l'on appelle des constantes de compilation
    C'est à dire qu'au même titre que les #define, elles ne prennent pas de place mémoire à l’exécution du programme, contrairement à l'utilisation de const ?

    3)
    Cela permet au compilateur d'effectuer des vérifications qu'il est tout à fait incapable d'effectuer lorsqu'on a recours aux autres techniques
    Tu parles des vérifications de types et de valeurs ?.. ou penses-tu à d'autres encore ?

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Steinvikel Voir le message
    j'ai peur de ne plus te suivre... une énumération nommé "couleur" est un type en soit, (presque) comme si j'avais utilisé typedef... ce que tu appel "valeur énuméré", je n'en suis pas sûr, est l'une des constantes entre accolade.
    Oui, tout à fait. En anglais on parle de enumerator. Ce qu'il faut comprendre, c'est que les termes BLANC, BLEU, ROUGE, VERT et les autres ne sont que des "symboles utilisés pour représenter une valeur". Comme nous sommes dans une énumération, on parle de valeurs énumérées (sous entendu : il y a sans doute plusieurs valeurs définies de la sorte )
    Je doit donc comprendre que ce qui se trouve dans l'énumération doit être de type intégraux (apparemment) et que l'usage de la variable "pot" qui est de type "couleur" n'accepte uniquement que des valeur de type "couleur" et aucun autre, qu'il soit fondamental ou dérivé.
    (ce qui expliquerait pourquoi "pot= 3;" ne compile pas)
    Exactement.
    Imperio, tu m'a préciser que, en gros, un élément d'énumération est un const int... dans mon exemple, un char est toléré à la compilation, y a-t-il promotion/conversion ?.. car je remarque qu'un caractère est utilisé dans l'enum mais qu'il n'est pas affiché.
    Le fait est que le type char n'est jamais aussi qu'une valeur numérique Ben oui, le processeur n'est capable de fonctionner qu'avec des valeurs binaires! Nous donnons un sens particulier à ces valeurs en fonctions de certaines conventions.

    La "convention" pour ce qui concerne les char, c'est que la valeur correspond à l'indice d'un "glyphe" (parce qu'il y a les lettre, les chiffres et quelques symboles ) dans ce que l'on appelle une table de caractères".

    Sauf indication contraire, la table utilisée est souvent la table ASCII (American Standard Code for Information Interchange). Du coup, le caractère '1' ne vaut pas 1, mais 49 parce qu'il s'agit du 50 glyphe de la table en question

    Evidemment, comme une valeur énumérée est, d'office, une valeur numérique entière, le fait d'avoir une valeur énumérée test='1' aura pour effet d'assimiler l'indice du caractères '1' (autrement dit 49) au symbole test
    koala01,
    1) Si j'ai bien compris (dans un autre sujet) une variable typé par enum est représenté par un type qui peut stocké n'importe lequel de sa liste, mais est considéré comme un type unique... "couleur" dans notre exemple.
    C'est bien cela.

    C'est pourquoi le compilateur se plaint dans ton exemple lorsque tu écrit pot = 3. Pour lui, 3 est un entier (sans doute un int, mais bon, il s’accommoderait encore bien du fait que ce soit un char, un short, un int, un long ou meme un long long, que ce soit en version signée ou non signée ), mais ce n'est pas une symbole reconnu comme faisant partie de l'énumération
    Cela pourait aussi bien être un unsigned short int comme un long long, ou un char.
    Ouaip!

    Pour la norme, la taille (comprends: le nombre de bits) nécessaire à la représentation d'une énumération doit uniquement être "suffisante pour représenter l'ensemble des valeurs énumérées".

    Mais ce genre d'imprécision est habituel dans la norme, afin de permettre les compilateurs de s'adapter à l'architecture qu'ils ciblent:
    Elle n'indique, par exemple, absolument pas le nombre de bits qui composent un byte. On parle donc d'octet par abus de langage en français parce qu'il n'y a strictement rien qui nous certifie qu'un byte soit bien composé de huit bits

    A partir de là, tout ce qu'elle nous impose, c'est:
    1. un byte == un char (ou un unsigned char)
    2. le char est l'unité "insécable" prise en compte pour les autres types (autrement dit, tu n'auras jamais un type qui aura une taille de 1.5
    3. l'inégalité suivante doit être respectée pour les entiers signés : char <= short <= int <= long <= long long
    4. l'inégalité suivante doit être respectée pour les entiers non signés : unsigned char <= unsigned short <= unsigned long <= unsigned long long

    A moins d'utiliser des types de taille précise (int16_t donne la garantie d'utiliser 16 bits), tu ne peux donc pas faire la moindre assertion quant à la taille des différents types. Parce qu'elle pourrait parfaitement changer sur une architecture particulière

    C++11 va même plus loin, avec les enum class que j''ai citées plus haut : elles permettent maintenant de définir la taille maximale susceptible de représenter les valeurs énumérées (et donc, de définir l'intervalle de valeur possibles):
    Code : 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
    enum class Test: signed char{
       un,
       deux,
       trois,
       ok = 127,
      // erreur = 128 : 128 est invalide pour un signed char (typiquement compris dans
     // l'intervalle [-127,128[
    };
    enum class Test: unsigned char{
       un,
       deux,
       trois,
       ok = 127,
       aussiOk = 255, //un unsigned char sera compris dans l'intervalle [0, 256[
       erreur = 256
    };
    Le compilateur t'informera de ton erreur avec un message proche de
    enumerator value XXX is too large for underrlying type 'le type utilisé'
    2)C'est à dire qu'au même titre que les #define, elles ne prennent pas de place mémoire à l’exécution du programme, contrairement à l'utilisation de const ?
    Attention! Toute valeur utilisée dans un programme prend de la place en mémoire, quelle que soit la manière dont cette valeur a été définie!

    C'est bien simpe: le processeur ne peut aller chercher des valeurs que dans la mémoire, si la valeur n’apparaît pas "quelque part" dans la mémoire, il ne va pas la déduire de "l'air du temps"

    Disons que, tout comme les #define, les valeurs énumérées sont des constantes, et qu'elle vont donc se trouver dans un segment mémoire "read only". C'est normalement aussi le cas avec les static const ou des chaines de caractères codées en dur" )

    Sauf erreur de ma part, le processeur y accédera au d'une adresse relative par rapport au début du segment de mémoire read-only dans lequel elle se trouve (l'adresse de début de ce segment mémoire étant, toujours sous réserve que je ne fasse pas d'erreur, souvent stocké directement dans un accu spécifique )

    Mais ce que je veux surtout dire, c'est que, si on reprend ton exemple, le compilateur assimilera directement le symbole BLEU à 0, l symbole BLANC à 1, le symbole ROUGE à 2 et le symbole TEST1 à 65 (ainsi que tous les autres à leurs valeurs respectives ), que ce sont des valeurs qui sont déjà connue au moment de la compilation (par opposition à une valeur qui devrait être lue dans un fichier ou introduite par l'utilisateur ).

    Mais il en va de même pour les valeurs définies au travers de #define, pour les valeurs statiques constantes (static const int valeur = XXX) et, de manière générale, pour toute valeur définie "en dur". A la différence près que, si tu ne définis pas spécifiquement une valeur pour l'une des valeur énumérées, il sera en mesure de la calculer lui même en incrémantant la valeur précédente (ou en utilisant 0 par défaut pour la toute première valeur énumérée de l'énumération )

    Et ca, il ne peut le faire qu'avec les valeurs énumérées (il n'y aurait aucun sens à lui permettre de le faire avec les static const ou avec les #define )
    3)Tu parles des vérifications de types et de valeurs ?.. ou penses-tu à d'autres encore ?
    Les deux, mon captiaine, et bien plus encore

    Je viens de t'expliquer l'une des choses que le compilateur est capable de faire lorsqu'il rencontre une énumération : Lorsqu'il rencontre les valeurs énumérées BLEU, BLANC et ROUGE, il est capable de leur donner une valeur précise, clairement définie et reproductible (0 pour BLEU, BLEU+1 pour BLANC, BLANC+1 pour ROUGE et ainsi de suite

    Il est également capable de vérifier le type de la donnée utilisé. Nous sommes dans un context "fortement typé" : le type couleur de ton exemple est un type clairement défini qui n'accepte que des valeurs clairement définies. Si tu utilises des valeurs différentes, le type est différent, et le compilateur s'en plaindra au prétexte qu'il est "incapable de convertir <tel type> en couleur" (pour reprendre ton exemple, toujours )

    Il connait la valeur numérique associée à la valeur énumérée. Il peut donc faire un tas de vérification sur cette valeur si le besoin s'en fait sentir.

    C'est en se basant sur la valeur numérique associée à la valeur énumérée que les enum class qui sont apparues en C++11 peuvent t'indiquer que la valeur est incompatible avec le type indiqué lorsque tu précise que tu veux que tes valeurs énumérées "tiennent" dans type particulier (l'exemple signed char, unsigned char que je t'ai montré plus tôt).

    Il la taille (comprends : le nombre de bits qui permettent de représenter l'ensemble des valeur énumérées) du type correspondant à l'énumération et peut l'utiliser : on peut récupérer une constante de compilation représentant cette taille avec l'opérateur sizeof, et on peut parfaitement utiliser cette taille dans un contexte de template
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename Type>
    class Truc{
        static const int size = sizeof(Type);
        /* ... */
    } ;
    int main(){
        std::cout <<Truc<couleur>::size<<std::endl
        return 0;
    }
    sera directement compilé sous la forme de std::cout<<4<<std::endl (parce que, chez moi, la taille des énumération est malgré tout de 4 bytes, tant que 32 bits suffisent, du moins)
    Par contre, le code (à peine modifié)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <typename Type>
    struct Truc{
       static const int size = sizeof(Type);
    };
    enum class couleur:char {BLEU, BLANC, ROUGE, VERT=3, TEST1='A', TEST2};
    int main(){
        std::cout<< Truc<couleur>::size<<std::endl;
    }
    sera directement compilé sous la forme de std::cout<<1<<std::endl

    Bref, il est parfaitement capable de faire énormément de choses, parfois très sympa, dont certaines ne sont possibles que parce que l'on manipule une énumération (bon, le dernier exemple, il y arrive avec n'importe quel type de donnée, mais quand meme : il peut le faire )

  8. #8
    Membre expert

    Profil pro
    activité : oui
    Inscrit en
    Janvier 2014
    Messages
    1 262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : activité : oui

    Informations forums :
    Inscription : Janvier 2014
    Messages : 1 262
    Points : 3 409
    Points
    3 409
    Par défaut
    merci pour tout ces petit correctif et ses explications.

    Je note qu'une énumération peut contenir pas mal d'énumérateurs de types différents, mais qu'il seront tous vu par le programme ou la compilation, comme étant des valeurs numériques... mais qu'il faut faire attention, TOUT les énumérateurs initialisés, DOIVENT L'ÊTRE EN DUR (comprendre: sans l'intermédiaire de variables).
    Les constantes énumérateurs peuvent être utilisé comme de simple constante ds le programme ou la compilation... mais qu'il faut faire attention, ce seront leur valeur numérique qui seront utilisé (attention au piège des 'caractère') ^^'

    Une question en amenant une autre...
    - Comment utiliser une énumération anonyme, puisqu'il n'y a pas de nom pour en typer une variable ?
    - peut-on spécifié le type d'un énumérateur ? (même si je le reconnais, à 1ère vu, ça n'a pas d'intérêt si le type max est indiqué)
    - les unions sont-elles à bannir ? (dans le cas d'un usage pour des types de valeurs admissible par des énumérations)

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Steinvikel Voir le message
    merci pour tout ces petit correctif et ses explications.

    Je note qu'une énumération peut contenir pas mal d'énumérateurs de types différents, mais qu'il seront tous vu par le programme ou la compilation, comme étant des valeurs numériques... mais qu'il faut faire attention, TOUT les énumérateurs initialisés, DOIVENT L'ÊTRE EN DUR (comprendre: sans l'intermédiaire de variables).
    C'est surtout que la valeur des énumérateurs est d'office fixée à la compilation. Soit parce qu'on précise directement une valeur particulière, soit parce que c'est la valeur correspondant à l'incrémentation de l'énumérateur précédant
    Les constantes énumérateurs peuvent être utilisé comme de simple constante ds le programme ou la compilation... mais qu'il faut faire attention, ce seront leur valeur numérique qui seront utilisé (attention au piège des 'caractère') ^^'
    C'est bien cela. Le "symbole" qui définit un énumérateur n'est qu'un "alias" de la valeur numérique
    Une question en amenant une autre...
    - Comment utiliser une énumération anonyme, puisqu'il n'y a pas de nom pour en typer une variable ?
    Tu serais étonné de savoir tout ce que l'on peut faire sans recourir à des variables

    Mais on entre essentiellement alors dans le domaine de la programmation générique.

    On peut, par exemple, envisager une classe template proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename T>
    class MyClass{
        static_assert(std::is_integer<T>::value,
                      "Only valids types are integer one");
        enum{
            min = std::numeric_limits<T>::min();
            max = std::numeric_limits<T>::max();
        }
    };
    Cela revient au même que de définir les constantes statiques min et max respectives (note que c'était surtout à la base un work arround utile suite à l'incompatibilité d'un compilateur particulier vis à vis de la norme )
    - peut-on spécifié le type d'un énumérateur ? (même si je le reconnais, à 1ère vu, ça n'a pas d'intérêt si le type max est indiqué)
    Avec les enum class issues de C++11, cela devient très facile.

    Avant, il était possible de définir le type qui devait servir pour la représentation à l'aide des suffixes spécifiques.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    enum myenum{
        one = 1<<1 LL, // on travaille d'office en (signed) long long
    }
    - les unions sont-elles à bannir ? (dans le cas d'un usage pour des types de valeurs admissible par des énumérations)
    Je ne suis, de manière générale, pas vraiment favorable à l'utilisation des unions. Je trouve qu'il y a "quelque chose de malsain" à vouloir faire en sorte que deux données, de type potentiellement différents, puissent partager le même espace mémoire.

    Sans oublier le fait que, normalement, tu devrais à chaque fois veiller à accéder au contenu de ton union qu'au travers de la dernière variable modifiée.

    Mais il faut reconnaitre que, dans certaines circonstances, elles représentent malgré tout une alternative cohérente . Personnellement, je ne me suis encore jamais trouvé dans une situation dans laquelle le recours à une union s'avérait indispensable (en C++ s'entend).

    Mais ceci dit, il n'y a strictement rien qui t'interdise de déclarer un champs de ton union du type d'une énumération. Ce n'est qu'une variable comme une autre, d'un type parfaitement déterminé et de taille clairement définie .

  10. #10
    Membre expert

    Profil pro
    activité : oui
    Inscrit en
    Janvier 2014
    Messages
    1 262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : activité : oui

    Informations forums :
    Inscription : Janvier 2014
    Messages : 1 262
    Points : 3 409
    Points
    3 409
    Par défaut
    ce topic arrive à ça fin =D
    Je rappel juste les quelques doutes restant et les questions en suspend puis tenterais d'incruster des exemples et modèles concrets correct dans le post #1.

    1) Le contexte d'utilisation d'énumération anonyme est particulier (je ne le connais pas), puisqu'il n'y a pas de nom pour en typer une variable.
    2) Les énumérations imbriqués ne sont pas permise, bien qu'elles appartiennent au même groupe de type que les constante admises.
    3) Des syntaxe alternatives pourraient exister pour déclarer des énumération... de la manière d'une structure, et de la déclaration de variables à la façon C++:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    enum leType {PARAMETRE1(a), /*...*/};   // à priori invalide (contrairement aux déclaration: "int nombre(451);" )
    //au lieu de:
    enum leType {PARAMETRE1=a, /*...*/};
     
    //ou encore
    enum leType {PARAMETRE1=a, /*...*/} laVariable;   // à priori valide (comme une structure)
    //au lieu de:
    enum leType {PARAMETRE1=a, /*...*/};
    leType laVariable;

Discussions similaires

  1. Quelles sont les limites d'oracle avec windows XP
    Par zintelix3d dans le forum Débuter
    Réponses: 13
    Dernier message: 29/05/2008, 16h09
  2. quelles sont les limites du mapping hibernate?
    Par jlassiramzy dans le forum Hibernate
    Réponses: 13
    Dernier message: 26/10/2007, 15h06
  3. [CLOB/BLOB] Quelles sont leurs utilités ?
    Par maximus001ma dans le forum DB2
    Réponses: 8
    Dernier message: 01/02/2006, 15h30
  4. Réponses: 2
    Dernier message: 13/10/2005, 19h04
  5. Quelles sont les limites de INTERBASE 7.5 ?
    Par lio33 dans le forum InterBase
    Réponses: 1
    Dernier message: 21/07/2005, 12h54

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