分享方式:


列舉 (C++)

列舉是使用者定義型別,其中包含一組具名的整數常數,稱為列舉程式

注意

本文包含 ISO Standard C++ 語言enum型別和限定範圍 (或強型別) enum class型別 (在 C++11 中推出)。 如需 C++/CLI 和 C++/CX 中public enum classprivate enum class型別的詳細資訊,請參閱 enum class(C++/CLI 和 C++/CX)

語法

enum-name
identifier

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

enum-head
enum-key attribute-specifier-seqopt enum-head-nameopt enum-baseopt

enum-head-name
nested-name-specifieropt identifier

opaque-enum-declaration
enum-key attribute-specifier-seqopt enum-head-name enum-baseopt ;

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
identifier attribute-specifier-seqopt

使用方式

// 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

參數

identifier
提供給列舉的類型名稱。

type
列舉值的基礎類型,所有列舉值都有相同的基礎類型。 可以是任何整數類資料類型。

enum-list
在列舉中,列舉值的逗號分隔清單。 範圍內的每個列舉值或變數名稱都必須是唯一的。 不過,值可以重複。 在不限範圍的列舉中,範圍是周圍範圍。在限定範圍的列舉中,範圍是enum-list本身。 在限定範圍列舉中,清單可能是空的,實際上會定義新的整數型別。

class
在宣告中使用此關鍵字,您就可以指定列舉已限定範圍,且必須提供identifier。 您也可以使用struct關鍵字取代class,因為它們在此內容中的語意相同。

列舉範圍

列舉會提供內容以描述表示為具名常數的值範圍。 這些具名常數也稱為列舉程式。 在原始 C 和 C++ enum型別中,不合格的列舉程式會顯示在宣告enum的整個範圍中。 在限定範圍列舉中,列舉程式名稱必須由enum型別名稱來限定。 下列範例示範這兩種列舉之間的這項基本差異:

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
        { /*...*/
        }
    }
}

列舉中的每個名稱被指派整數值,這個值對應於它在列舉值順序中的位置。 根據預設,第一個指派的值是 0,下一個指派的是 1,依此類推,不過,您可以明確設定列舉程式的值,如下所示:

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

指派給列舉值 Diamonds 的值為 1。 後續列舉程式,如果未提供其明確值,會接收前一個列舉程式的值加一。 在上述範例中,Hearts 會有值 2,而 Clubs 會有 3,依此類推。

每個列舉程式會被視為常數,而且在定義enum的範圍內 (適用於不限範圍的列舉) 或在enum本身內 (適用於限定範圍列舉) 必須有唯一名稱。 指定給名稱的值不需要是唯一的。 例如,考慮這個不限範圍的列舉的宣告Suit

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

然後 DiamondsHeartsClubsSpades 的值分別為 5、6、4 和 5。 請注意 5 已多次使用,這是允許的行為,即使可能不適合。 對於限定範圍列舉,這些規則都相同。

轉型規則

不限範圍的列舉常數可以隱含轉換為int,但是int絕對不能隱含轉換為列舉值。 下列範例顯示,如果您嘗試將不是Suit的值指派給hand時,所發生的狀況:

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

必須有轉型將int轉換為限定範圍列舉程式或不限範圍的列舉程式。 不過,您可以將不限範圍的列舉程式提升為整數值,而不需要轉型。

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

以這種方式使用隱含轉換,可能造成非預期的副作用。 為了協助排除與不限範圍的列舉關聯之程式設計錯誤,範圍列舉值是強類型。 限定範圍的列舉程式必須由列舉型別名稱 (識別碼) 來限定,且無法以隱含方式轉換為,如下列範例所示:

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
    }
}

請注意,程式行 hand = account_num; 仍然會導致因為不限範圍的列舉而發生的錯誤,如上所示。 使用明確轉換時,允許這個行為。 不過,使用限定範圍列舉時,不再允許在沒有明確轉型的情況下,於下一個陳述式 account_num = Suit::Hearts; 中嘗試轉換。

沒有列舉程式的列舉

Visual Studio 2017 15.3 版和更新版本 (可搭配 /std:c++17 及更新版本使用):藉由定義具有明確基礎型別和沒有列舉程式的列舉 (一般或限定範圍),您實際上可以引進沒有隱含轉換成任何其他型別的新整數型別。 藉由使用此型別,而不是其內建基礎型別,您可以消除因不小心隱含轉換所造成的細微錯誤。

enum class byte : unsigned char { };

新型別是基礎型別的確切復本,因此具有相同的呼叫慣例,這表示它可以跨 ABIS 使用,而不會造成任何效能損失。 使用直接清單初始化初始化型別變數時,不需要轉換。 下列範例會示範如何在各種內容中初始化沒有列舉程式的列舉:

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;
}

另請參閱

C 列舉宣告
關鍵字