Le enumerazioni C ++ sono firmate o non firmate? E per estensione è sicuro convalidare un input verificando che sia <= il tuo valore massimo e tralasciare> = il tuo valore minimo (supponendo che tu abbia iniziato da 0 e incrementato di 1)?
Le enumerazioni C ++ sono firmate o non firmate? E per estensione è sicuro convalidare un input verificando che sia <= il tuo valore massimo e tralasciare> = il tuo valore minimo (supponendo che tu abbia iniziato da 0 e incrementato di 1)?
Risposte:
Non dovresti fare affidamento su alcuna rappresentazione specifica. Leggi il seguente link . Inoltre, lo standard afferma che è definito dall'implementazione quale tipo integrale viene utilizzato come tipo sottostante per un'enumerazione, tranne per il fatto che non deve essere maggiore di int, a meno che un valore non possa adattarsi a int o un int senza segno.
In breve: non puoi fare affidamento sul fatto che un'enumerazione sia firmata o non firmata.
Andiamo alla fonte. Ecco cosa dice il documento dello standard C ++ 03 (ISO / IEC 14882: 2003) in 7.2-5 (Dichiarazioni di enumerazione):
Il tipo sottostante di un'enumerazione è un tipo integrale che può rappresentare tutti i valori dell'enumeratore definiti nell'enumerazione. È definito dall'implementazione quale tipo integrale viene utilizzato come tipo sottostante per un'enumerazione, tranne per il fatto che il tipo sottostante non deve essere maggiore di int a meno che il valore di un enumeratore non possa rientrare in un int o unsigned int.
In breve, il tuo compilatore può scegliere (ovviamente, se hai numeri negativi per alcuni dei tuoi valori di ennumerazione, sarà firmato).
Non dovresti dipendere dal fatto che siano firmati o non firmati. Se desideri renderli firmati o non firmati in modo esplicito, puoi utilizzare quanto segue:
enum X : signed int { ... }; // signed enum
enum Y : unsigned int { ... }; // unsigned enum
Non dovresti fare affidamento sul fatto che sia firmato o non firmato. Secondo lo standard è definito dall'implementazione quale tipo integrale viene utilizzato come tipo sottostante per un'enumerazione. Nella maggior parte delle implementazioni, tuttavia, è un numero intero con segno.
In C ++ 0x verranno aggiunte enumerazioni fortemente tipizzate che ti permetteranno di specificare il tipo di enumerazione come:
enum X : signed int { ... }; // signed enum
enum Y : unsigned int { ... }; // unsigned enum
Anche ora, tuttavia, è possibile ottenere una semplice convalida utilizzando enum come variabile o tipo di parametro come questo:
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.
Il compilatore può decidere se le enumerazioni sono firmate o non firmate.
Un altro metodo per convalidare le enumerazioni consiste nell'usare l'enumerazione stessa come tipo di variabile. Per esempio:
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.
Anche alcune vecchie risposte hanno ottenuto 44 voti positivi, tendo a non essere d'accordo con tutti loro. In breve, non credo che dovremmo preoccuparci underlying type
dell'enumerazione.
Prima di tutto, il tipo Enum C ++ 03 è un tipo distinto a sé stante che non ha il concetto di segno. Dal momento che dallo standard C ++ 03dcl.enum
7.2 Enumeration declarations
5 Each enumeration defines a type that is different from all other types....
Quindi, quando parliamo del segno di un tipo enum, diciamo quando confrontiamo 2 operandi enum usando l' <
operatore, stiamo effettivamente parlando della conversione implicita del tipo enum in un tipo integrale. È il segno di questo tipo integrale che conta . E quando si converte enum in tipo integrale, si applica questa istruzione:
9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).
E, a quanto pare, il tipo sottostante dell'enumerazione non ha nulla a che fare con la Promozione Integrale. Poiché lo standard definisce la promozione integrale in questo modo:
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.
Quindi, se un tipo di enumerazione diventa signed int
o unsigned int
dipende dal fatto che signed int
possa contenere tutti i valori degli enumeratori definiti, non il tipo sottostante dell'enumerazione.
Vedere la mia domanda correlata Segno del tipo di enumerazione C ++ non corretto dopo la conversione in tipo integrale
-Wsign-conversion
. Lo usiamo per aiutare a rilevare errori non intenzionali nel nostro codice. Ma +1 per citare lo standard e sottolineare che un enum non ha alcun tipo ( signed
contro unsigned
) ad esso associato.
In futuro, con C ++ 0x, saranno disponibili enumerazioni fortemente tipizzate e avranno diversi vantaggi (come l'indipendenza dai tipi, i tipi sottostanti espliciti o l'ambito esplicito). Con ciò potresti essere più sicuro del segno del tipo.
Oltre a ciò che altri hanno già detto su firmato / non firmato, ecco cosa dice lo standard sull'intervallo di un tipo enumerato:
7.2 (6): "Per un'enumerazione in cui e (min) è l'enumeratore più piccolo ed e (max) è il più grande, i valori dell'enumerazione sono i valori del tipo sottostante nell'intervallo da b (min) a b (max ), dove b (min) eb (max) sono, rispettivamente, i valori più piccolo e più grande del campo di bit più piccolo che può memorizzare e (min) ed e (max). È possibile definire un'enumerazione che ha valori non definiti da uno qualsiasi dei suoi enumeratori. "
Quindi per esempio:
enum { A = 1, B = 4};
definisce un tipo enumerato dove e (min) è 1 ed e (max) è 4. Se il tipo sottostante è firmato int, il campo di bit richiesto più piccolo ha 4 bit e se gli int nella tua implementazione sono complemento a due, l'intervallo valido di l'enumerazione va da -8 a 7. Se il tipo sottostante non è firmato, ha 3 bit e l'intervallo è compreso tra 0 e 7. Se ti interessa, controlla la documentazione del compilatore (ad esempio se desideri eseguire il cast di valori integrali diversi dagli enumeratori tipo enumerato, quindi è necessario sapere se il valore è compreso nell'intervallo dell'enumerazione o meno, in caso contrario il valore dell'enumerazione risultante non è specificato).
Se questi valori sono un input valido per la tua funzione potrebbe essere un problema diverso dal fatto che siano valori validi del tipo enumerato. Il tuo codice di controllo è probabilmente preoccupato per il primo piuttosto che per il secondo, quindi in questo esempio dovresti almeno controllare> = A e <= B.
Controllalo con std::is_signed<std::underlying_type
+ enumerazioni con ambito predefinito suint
https://en.cppreference.com/w/cpp/language/enum implica:
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>()));
}
Compila ed esegui:
g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main
Produzione:
0
Testato su Ubuntu 16.04, GCC 6.4.0.