Enumerazioni (C++)

Un'enumerazione è un tipo definito dall'utente costituito da un set di costanti integrali denominate note come enumeratori.

Nota

Questo articolo illustra il tipo di linguaggio enum C++ standard ISO e il tipo con ambito (o fortemente tipizzato) enum class introdotto in C++11. Per informazioni sui public enum class tipi o private enum class in C++/CLI e C++/CX, vedere enum class (C++/CLI e C++/CX).

Sintassi

enum-name:
identifier

enum-specifier:
enum-head{enumerator-listopt}
enum-head { enumerator-list , }

enum-head:
enum-keyattribute-specifier-seqoptenum-head-nameoptenum-base

enum-head-name:
nested-name-specifieroptidentifier

opaque-enum-declaration:
enum-keyattribute-specifier-seqopt opt optenum-head-nameenum-base;

enum-key:
enum
enum class
enum struct

enum-base:
: type-specifier-seq

enumerator-list:
enumerator-definition
enumerator-list , enumerator-definition

enumerator-definition:
enumerator
enumerator = constant-expression

enumerator:
identifierattribute-specifier-seqopt

Utilizzo

// unscoped enum:
// enum [identifier] [: type] {enum-list};

// scoped enum:
// enum [class|struct] [identifier] [: type] {enum-list};

// Forward declaration of enumerations  (C++11):
enum A : int;          // non-scoped enum must have type specified
enum class B;          // scoped enum defaults to int but ...
enum class C : short;  // ... may have any integral underlying type

Parametri

identifier
Nome del tipo assegnato all'enumerazione.

type
Tipo sottostante degli enumeratori. Tutti gli enumeratori dispongono dello stesso tipo sottostante. Può essere un tipo integrale qualsiasi.

enum-list
Elenco degli enumeratori nell'enumerazione separati da virgola. Ogni enumeratore o nome della variabile nell'ambito deve essere univoco. I valori possono essere tuttavia duplicati. In un'enumerazione senza ambito, l'ambito è l'ambito circostante; in un'enumerazione con ambito, l'ambito è lo enum-list stesso. In un'enumerazione con ambito, l'elenco può essere vuoto, che in effetti definisce un nuovo tipo integrale.

class
Usando questa parola chiave nella dichiarazione, è necessario specificare l'ambito dell'enumerazione e specificare un oggetto identifier . È anche possibile usare la struct parola chiave al posto di class, perché sono semanticamente equivalenti in questo contesto.

Ambito dell'enumeratore

Un'enumerazione fornisce contesto per descrivere un intervallo di valori rappresentati come costanti denominate. Queste costanti denominate sono denominate anche enumeratori. Nei tipi C e C++ enum originali, gli enumeratori non qualificati sono visibili nell'ambito in cui viene dichiarato .enum Nelle enumerazioni con ambito, il nome dell'enumeratore deve essere qualificato dal nome del enum tipo. Nell'esempio seguente viene illustrata questa differenza di base tra i due tipi di enumerazioni:

namespace CardGame_Scoped
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Suit::Clubs) // Enumerator must be qualified by enum type
        { /*...*/}
    }
}

namespace CardGame_NonScoped
{
    enum Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Clubs) // Enumerator is visible without qualification
        { /*...*/
        }
    }
}

A ogni nome nell'enumerazione viene assegnato un valore integrale che corrisponde alla posizione di tale valore nell'ordine dei valori nell'enumerazione. Per impostazione predefinita, al primo valore viene assegnato 0, a quello successivo viene assegnato 1 e così via, ma è possibile impostare in modo esplicito il valore di un enumeratore, come illustrato di seguito:

enum Suit { Diamonds = 1, Hearts, Clubs, Spades };

All'enumeratore Diamonds viene assegnato al valore 1. Gli enumeratori successivi, se non hanno un valore esplicito, ricevono il valore dell'enumeratore precedente più uno. Nell'esempio precedente Hearts avrebbe il valore 2, Clubs avrebbe il valore 3 e così via.

Ogni enumeratore viene considerato come costante e deve avere un nome univoco all'interno dell'ambito in cui enum è definito (per enumerazioni senza ambito) o all'interno di enum se stesso (per le enumerazioni con ambito). I valori assegnati ai nomi non devono essere univoci. Si consideri ad esempio questa dichiarazione di un'enumerazione Suitsenza ambito:

enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };

I valori di Diamonds, Hearts, Clubse Spades sono rispettivamente 5, 6, 4 e 5. Si noti che 5 viene usato più volte; è consentito anche se potrebbe non essere previsto. Queste regole sono identiche a quelle delle enumerazioni con ambito.

Regole di cast

Le costanti enumerazioni senza ambito possono essere convertite in modo implicito in int, ma un oggetto int non è mai convertibile in modo implicito in un valore di enumerazione. L'esempio seguente illustra cosa accade se si tenta di assegnare hand un valore che non è un Suit:

int account_num = 135692;
Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'

Per convertire un oggetto int in un enumeratore con ambito o senza ambito, è necessario un cast. Tuttavia, è possibile alzare di livello un enumeratore senza ambito a un valore intero senza un cast.

int account_num = Hearts; //OK if Hearts is in an unscoped enum

L'uso di conversioni implicite in questo modo può generare effetti collaterali imprevisti. Per eliminare gli errori di programmazione associati alle enumerazioni senza ambito, i valori delle enumerazioni con ambito sono fortemente tipizzati. Gli enumeratori con ambito devono essere qualificati dal nome del tipo di enumerazione (identificatore) e non possono essere convertiti in modo implicito, come illustrato nell'esempio seguente:

namespace ScopedEnumConversions
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void AttemptConversions()
    {
        Suit hand;
        hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
        hand = Suit::Clubs; //Correct.
        int account_num = 135692;
        hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
        hand = static_cast<Suit>(account_num); // OK, but probably a bug!!!

        account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'
        account_num = static_cast<int>(Suit::Hearts); // OK
    }
}

Notare che la riga hand = account_num; causa ancora l'errore che si verifica con le enumerazioni senza ambito, come illustrato in precedenza. È consentito con un cast esplicito. Tuttavia, con le enumerazioni con ambito, la conversione tentata nell'istruzione successiva, account_num = Suit::Hearts;, non è più consentita senza un cast esplicito.

Enumerazioni senza enumeratori

Visual Studio 2017 versione 15.3 e successive (disponibile con /std:c++17 e versioni successive): definendo un'enumerazione (normale o con ambito) con un tipo sottostante esplicito e nessun enumeratore, è possibile introdurre un nuovo tipo integrale che non ha alcuna conversione implicita in altri tipi. Usando questo tipo anziché il tipo sottostante predefinito, è possibile eliminare il rischio di errori sottili causati da conversioni implicite accidentali.

enum class byte : unsigned char { };

Il nuovo tipo è una copia esatta del tipo sottostante e pertanto ha la stessa convenzione di chiamata, il che significa che può essere usato in abi senza alcuna penalità delle prestazioni. Non è necessario alcun cast quando le variabili del tipo vengono inizializzate usando l'inizializzazione direct-list. Nell'esempio seguente viene illustrato come inizializzare le enumerazioni senza enumeratori in vari contesti:

enum class byte : unsigned char { };

enum class E : int { };
E e1{ 0 };
E e2 = E{ 0 };

struct X
{
    E e{ 0 };
    X() : e{ 0 } { }
};

E* p = new E{ 0 };

void f(E e) {};

int main()
{
    f(E{ 0 });
    byte i{ 42 };
    byte j = byte{ 42 };

    // unsigned char c = j; // C2440: 'initializing': cannot convert from 'byte' to 'unsigned char'
    return 0;
}

Vedi anche

Dichiarazioni di enumerazione C
Parole chiave