Question

Les énumérations C ++ sont-elles signées ou non signées? Et par extension, est-il prudent de valider une entrée en vérifiant qu’elle est & Lt; = votre valeur maximale, et de laisser de côté & Gt; = votre valeur minimale (en supposant que vous avez commencé à 0 et incrémenté de 1)?

Était-ce utile?

La solution

Vous ne devriez pas vous fier à une représentation spécifique. Lisez le lien suivant. En outre, la norme indique que le type intégral utilisé est le type sous-jacent d'une énumération défini par l'implémentation, sauf qu'il ne doit pas être supérieur à int, sauf si une valeur ne peut pas correspondre à int ou à un entier non signé.

En bref: vous ne pouvez pas compter sur une énumération signée ou non signée.

Autres conseils

Allons à la source. Voici ce que dit le document de la norme C ++ 03 (ISO / IEC 14882: 2003) dans 7.2-5 (Déclarations d’énumération):

  

Le type sous-jacent d'une énumération   est un type intégral qui peut représenter   toutes les valeurs d'énumérateur définies dans   l'énumération. Il est   défini par l'implémentation   type est utilisé comme type sous-jacent   pour une énumération, sauf que le   le type sous-jacent ne doit pas être plus grand   que int sauf si la valeur d'un   énumérateur ne peut pas tenir dans un int ou   non signé int.

En bref, votre compilateur doit choisir (évidemment, si vous avez des nombres négatifs pour certaines de vos valeurs d’énumération, il sera signé).

Vous ne devriez pas compter sur leur signature ou leur non signature. Si vous souhaitez les faire signer explicitement ou non, vous pouvez utiliser les éléments suivants:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

Vous ne devez pas vous fier à ce qu’il soit signé ou non signé. Selon la norme, le type intégral utilisé est le type sous-jacent pour une énumération. Cependant, dans la plupart des implémentations, il s’agit d’un entier signé.

En C ++ 0x, des énumérations fortement typées seront ajoutées qui vous permettra de spécifier le type d'une énumération telle que:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

Même maintenant, cependant, une validation simple peut être obtenue en utilisant l'enum comme type de variable ou de paramètre comme ceci:

enum Fruit { Apple, Banana };

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit
                    // even though it has the same value as banana.

Le compilateur peut décider si les énumérations sont signées ou non.

Une autre méthode de validation des énumérations consiste à utiliser l’énum lui-même en tant que type de variable. Par exemple:

enum Fruit
{
    Apple = 0,
    Banana,
    Pineapple,
    Orange,
    Kumquat
};

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit even though it has the same value as banana.

Même certaines réponses anciennes ont obtenu 44 votes positifs, j'ai tendance à être en désaccord avec chacun d'eux. En bref, je ne pense pas que nous devrions nous soucier de la underlying type de l'énum.

Tout d’abord, le type C ++ 03 Enum est un type distinct qui n’a pas de concept de signe. Depuis C ++ 03 standard dcl.enum

7.2 Enumeration declarations 
5 Each enumeration defines a type that is different from all other types....

Ainsi, lorsque nous parlons du signe d'un type enum, disons, lorsque nous comparons deux opérandes enum à l'aide de l'opérateur <, nous parlons en fait de convertir implicitement le type enum en un type intégral. C’est le signe de ce type intégral qui compte . Et lors de la conversion d’enum en type intégral, cette déclaration s’applique:

9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).

Et, apparemment, le type sous-jacent de l'énum n'a rien à voir avec la promotion intégrale. Comme la norme définit la promotion intégrale comme suit:

4.5 Integral promotions conv.prom
.. An rvalue of an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration
(i.e. the values in the range bmin to bmax as described in 7.2: int, unsigned int, long, or unsigned long.

Ainsi, le type d’énumération devient signed int ou unsigned int dépend de si <=> peut contenir toutes les valeurs des énumérateurs définis, et non le type sous-jacent de l’énum.

Voir ma question connexe Type de type Enum C ++ incorrect après la conversion en intégration Tapez

Dans le futur, avec C ++ 0x, énumérations fortement typées sera disponible et présentera plusieurs avantages (tels que la sécurité de type, les types sous-jacents explicites ou la portée explicite). Avec cela, vous pourriez être mieux assuré du signe du type.

Outre ce que d'autres ont déjà dit à propos de signés / non signés, voici ce que dit la norme à propos de la plage d'un type énuméré:

7.2 (6): & "; Pour une énumération où e (min) est le plus petit énumérateur et e (max) est le plus grand, les valeurs de l'énumération sont les valeurs du type sous-jacent dans l'intervalle b (min) à b (max), où b (min) et b (max) sont, respectivement, les valeurs les plus petites et les plus grandes du plus petit champ binaire pouvant stocker e (min) et e (max). Il est possible de définir une énumération dont les valeurs ne sont définies par aucun de ses énumérateurs. & Quot;

Ainsi, par exemple:

enum { A = 1, B = 4};

définit un type énuméré où e (min) vaut 1 et e (max) 4. Si le type sous-jacent est signé int, le plus petit champ binaire requis a 4 bits, et si ints dans votre implémentation sont des compléments à deux, le la plage valide de l'énumération est comprise entre -8 et 7. Si le type sous-jacent est non signé, il est composé de 3 bits et la plage est compris entre 0 et 7. Consultez la documentation de votre compilateur si vous en tenez compte (par exemple, si vous souhaitez attribuer des valeurs intégrales autres que énumérateurs du type énuméré, vous devez savoir si la valeur est dans la plage de l’énumération ou non - si ce n’est pas la valeur de l’énum qui en résulte est non spécifiée).

Le fait de savoir si ces valeurs sont des entrées valides pour votre fonction peut être un problème différent de celui de savoir si elles sont des valeurs valides du type énuméré. Votre code de contrôle concerne probablement le premier plutôt que le second, et dans cet exemple, vérifiez au moins & Gt; = A et & Lt; = B.

Vérifiez-le avec std::is_signed<std::underlying_type les étendues étendues définies par défaut sur int

.

https://en.cppreference.com/w/cpp/language/enum implique:

main.cpp

#include <cassert>
#include <iostream>
#include <type_traits>

enum Unscoped {};
enum class ScopedDefault {};
enum class ScopedExplicit : long {};

int main() {
    // Implementation defined, let's find out.
    std::cout << std::is_signed<std::underlying_type<Unscoped>>() << std::endl;

    // Guaranteed. Scoped defaults to int.
    assert((std::is_same<std::underlying_type<ScopedDefault>::type, int>()));

    // Guaranteed. We set it ourselves.
    assert((std::is_same<std::underlying_type<ScopedExplicit>::type, long>()));
}

Compiler et exécuter:

g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main

Sortie:

0

Testé sur Ubuntu 16.04, GCC 6.4.0.

scroll top