web-dev-qa-db-fra.com

Les énumérations C++ sont-elles signées ou non signées?

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 <= votre valeur maximale et d’omettre> = votre valeur minimale (en supposant que vous avez commencé à 0 et augmenté de 1)?

97
Matt

Vous ne devriez pas compter sur une représentation spécifique. Lisez le suivant link . De plus, 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, à moins qu'une valeur ne puisse entrer dans int ou non signé.

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

60
zvrba

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 entier pouvant représenter toutes les valeurs d'énumérateur définies dans l'énumération. Il est défini par l'implémentation, quelle intégrale 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 l'é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é).

97
Michael Burr

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

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

Vous ne devriez pas compter sur le fait 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 les énumérations fortement typées seront ajoutées, ce 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 variable ou type 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.
14
Matt

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

Tout d’abord, le type C++ 03 Enum est un type distinct qui n’a aucune notion 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 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 aucun rapport avec la promotion intégrale. Puisque le standard définit la promotion intégrale comme ceci:

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

Voir la question connexe Signe du type Enum C++ incorrect après la conversion en type intégral

5
JavaMan

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

Une autre méthode de validation des énumérations consiste à utiliser l’énumisme 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.
5
Cristián Romo

Outre ce que d'autres ont déjà dit à propos de signé/non signé, 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) 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. "

Donc par exemple:

enum { A = 1, B = 4};

définit un type énuméré où e(min) est égal à 1 et e(max) est égal à 4. Si le type sous-jacent est signé int, le plus petit champ binaire requis a 4 bits, et s'il est inte dans votre implémentation deux sont un complément à deux alors la plage valide de l'énumération est comprise entre -8 et 7. Si le type sous-jacent n'est pas signé, il comporte 3 bits et la plage est compris entre 0 et 7. Consultez la documentation de votre compilateur si vous le souhaitez (par exemple si vous voulez Si vous convertissez des valeurs intégrales autres que des énumérateurs dans le type énuméré, vous devez savoir si la valeur se situe 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).

La question de savoir si ces valeurs sont des entrées valides pour votre fonction peut être un problème différent de celui de savoir s'il s'agit de valeurs valides du type énuméré. Votre code de contrôle concerne probablement le premier plutôt que le second. Dans cet exemple, vérifiez donc au moins>> A et <= B.

4
Steve Jessop

À l'avenir, avec C++ 0x, les énumérations fortement typées seront disponibles et présenteront 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.

4
Kris Kumler

Vérifiez-le avec std::is_signed<std::underlying_type + les étendues étendues à 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>()));
}

GitHub en amont .

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.