通过


枚举(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 选择}
enum-head { enumerator-list , }

enum-head:
enum-key attribute-specifier-seq 选择enum-head-name选择enum-base选择

enum-head-name:
nested-name-specifier 选择identifier

opaque-enum-declaration:
enum-key attribute-specifier-seq 选择enum-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:
identifier attribute-specifier-seq 选择

用法

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

Diamonds Hearts、、ClubsSpades 5 的值分别为 5、6、4 和 5。 请注意,5 多次使用;即使它可能不是预期,它也是允许的。 对于作用域枚举,这些规则是相同的。

强制转换规则

可以隐式转换为无作用域枚举常量,但int永远不会隐式转换为int枚举值。 以下示例演示了尝试分配 handSuit值时会发生什么情况:

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

新类型是基础类型的精确副本,因此具有相同的调用约定,这意味着它可以在 API 之间使用,而不会造成任何性能损失。 使用直接列表初始化初始化类型变量时,不需要强制转换。 以下示例演示如何在各种上下文中初始化没有枚举器的枚举:

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 枚举声明
关键字