列舉 (C++)
列舉是由一組稱為 列舉值之具名整數常數所組成的使用者定義型別 。
注意
本文涵蓋 ISO 標準 C++ 語言 enum
類型和 C++11 中引進的範圍(或強型別) enum class
類型。 如需 C++/CLI 和 C++/CX 中 或 private enum class
類型的相關資訊 public enum class
,請參閱 enum class
(C++/CLI 和 C++/CX)。
語法
enum-name
:
identifier
enum-specifier
:
enum-head
{
enumerator-list
opt}
enum-head
{
enumerator-list
,
}
enum-head
:
enum-key
attribute-specifier-seq
opt opt opt enum-base
enum-head-name
enum-head-name
:
nested-name-specifier
optidentifier
opaque-enum-declaration
:
enum-key
attribute-specifier-seq
opt opt enum-head-name
enum-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
:
identifier
attribute-specifier-seq
opt
使用方式
// 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 };
、 Hearts
Clubs
和 Spades
的值 Diamonds
分別為 5、6、4 和 5。 請注意,5 已多次使用;即使它可能不適合,還是允許它。 對於限定範圍列舉,這些規則都相同。
轉型規則
未範圍列舉常數可以隱含地轉換成 int
,但 int
永遠無法隱含轉換成列舉值。 下列範例顯示如果您嘗試指派 hand
不是 Suit
的值,會發生什麼情況:
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
以這種方式使用隱含轉換,可能造成非預期的副作用。 為了協助排除與不限範圍的列舉關聯之程式設計錯誤,範圍列舉值是強類型。 範圍列舉值必須以列舉類型名稱 (identifier) 限定,且無法隱含轉換,如下列範例所示:
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;
}
另請參閱
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應