Namespaces
Variants

Enumeration declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

一个 枚举 是一种独特类型,其值被限制在一定范围内(详见下文),该范围可能包含若干显式命名的常量(" 枚举项 ")。

常量的值是被称为枚举 底层类型 的整数类型的值。枚举具有与其底层类型相同的 大小 值表示 对齐要求 。此外,枚举的每个值都具有与底层类型对应值相同的表示形式。

枚举通过以下语法(重新)声明:

枚举键 属性  (可选) 枚举头名称  (可选) 枚举基类型  (可选)
{ 枚举列表  (可选) }
(1)
枚举键 属性  (可选) 枚举头名称  (可选) 枚举基类型  (可选)
{ 枚举列表 , }
(2)
枚举键 属性  (可选) 枚举头名称 枚举基类型  (可选) ; (3) (自 C++11 起)
1) enum-specifier ,出现在 声明 语法的 decl-specifier-seq 中:定义枚举类型及其枚举项。
2) 枚举列表末尾可以跟随一个尾随逗号。
3) 不透明枚举声明:定义枚举类型但不定义其枚举项:在此声明之后,该类型成为完整类型且其大小已知。
enum-key -

enum

(C++11 前)

enum enum class enum struct 之一

(C++11 起)
attr - (C++11 起) 任意数量的 属性 的可选序列
enum-head-name -

正在声明的枚举名称,可以省略。

(C++11 前)

正在声明的枚举名称,可选择性地以 nested-name-specifier 开头:由名称和范围解析运算符 :: 组成的序列,以范围解析运算符结尾。仅可在非限定作用域的非透明枚举声明中省略。
仅当枚举名称存在且此声明为重新声明时, nested-name-specifier 方可出现。对于透明枚举声明, nested-name-specifier 仅可在 显式特化声明 中的枚举名称前出现。
若存在 nested-name-specifier ,则 enum-specifier 不能引用仅通过 using 声明 继承或引入的枚举,且 enum-specifier 仅可出现在包含先前声明的命名空间中。在此情况下, nested-name-specifier 不能以 decltype 说明符开头。

(C++11 起)
enum-base - (C++11 起) 冒号 ( : ),后跟命名整数类型的 type-specifier-seq (若具有 cv 限定符,则忽略限定符),该类型将作为此枚举类型的固定底层类型
enumerator-list - 以逗号分隔的枚举器定义列表,每个定义要么是唯一的 identifier (成为枚举器的名称),要么是带有常量表达式的唯一标识符: identifier = constant-expression 在任一情况下, identifier 可直接后接可选的 属性说明符序列 (C++17 起)

枚举有两种不同的类型: 无作用域枚举 (使用 enum-key enum 声明)和 有作用域枚举 (使用 enum-key enum class enum struct 声明)。

目录

无作用域枚举

enum 名称  (可选) { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... } (1)
enum 名称  (可选) : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... } (2) (C++11 起)
enum 名称 : 类型 ; (3) (C++11 起)
1) 声明一个底层类型不固定的无作用域枚举类型(此时底层类型是实现定义的能够表示所有枚举值的整型;该类型不会大于 int ,除非枚举值无法容纳于 int unsigned int 。若 enumerator-list 为空,则底层类型如同该枚举仅含值为 0 的单个枚举值。若无任何整型能表示所有枚举值,则该枚举非良构)。
2) 声明一个具有固定底层类型的无作用域枚举类型。
3) 非限定作用域枚举的不透明枚举声明必须指定名称和底层类型。

每个 enumerator 都会成为枚举类型的命名常量(即 name ),在封闭作用域中可见,并且可以在需要常量时使用。

enum Color { red, green, blue };
Color r = red;
switch(r)
{
    case red  : std::cout << "red\n";   break;
    case green: std::cout << "green\n"; break;
    case blue : std::cout << "blue\n";  break;
}

每个枚举项都与底层类型的一个值相关联。当在 枚举列表 中提供 = 时,枚举项的值由这些关联的 常量表达式 定义。如果第一个枚举项没有 = ,其关联值为零。对于任何其他定义中没有 = 的枚举项,其关联值为前一个枚举项的值加一。

enum Foo { a, b, c = 10, d, e = 1, f, g = f + c };
//a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12

无作用域枚举的 名称 可以省略:此类声明仅将枚举项引入到封闭作用域中:

enum { a, b, c = 0, d = a + 2 }; // 定义 a = 0, b = 1, c = 0, d = 2

当非限定作用域枚举作为类的成员时,其枚举项可通过类成员访问运算符 . -> 进行访问:

struct X
{
    enum direction { left = 'l', right = 'r' };
};
X x;
X* p = &x;
int a = X::direction::left; // 仅允许在 C++11 及之后版本中使用
int b = X::left;
int c = x.left;
int d = p->left;

声明说明符 成员声明 中,序列

enum enum-head-name :

始终被解析为枚举声明的一部分:

struct S
{
    enum E1 : int {};
    enum E1 : int {}; // 错误:枚举重声明,
                      // 不会被解析为 enum E1 类型的零长度位域
};
enum E2 { e1 };
void f()
{
    false ? new enum E2 : int(); // 正确:'int' 不会被解析为基础类型
}
(C++11 起)

用于链接目的的枚举名称

对于没有 链接用途typedef名称 且包含枚举项的未命名枚举,在 链接用途 上通过其底层类型和首个枚举项进行标识;此类枚举被称为具有作为 链接用途名称 的枚举项。

作用域枚举

enum struct|class 名称 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... } (1)
enum struct|class 名称 : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... } (2)
enum struct|class 名称 ; (3)
enum struct|class 名称 : 类型 ; (4)
1) 声明底层类型为 int 的作用域枚举类型(关键字 class struct 完全等价)
2) 声明底层类型为 类型 的作用域枚举类型
3) 底层类型为 int 的作用域枚举的不透明枚举声明
4) 底层类型为 类型 的作用域枚举的不透明枚举声明

每个 枚举项 成为枚举类型(即 名称 )的命名常量,它们包含在枚举的作用域内,可通过作用域解析运算符访问。作用域枚举器的值不能隐式转换为整型,但可以使用 static_cast 获取枚举器的数值。

#include <iostream>
int main()
{
    enum class Color { red, green = 20, blue };
    Color r = Color::blue;
    switch(r)
    {
        case Color::red  : std::cout << "red\n";   break;
        case Color::green: std::cout << "green\n"; break;
        case Color::blue : std::cout << "blue\n";  break;
    }
    // int n = r; // 错误:无法从作用域枚举隐式转换为 int
    int n = static_cast<int>(r); // 正确,n = 21
    std::cout << n << '\n'; // 输出 21
}
(C++11 起)

枚举类型在满足以下所有条件时,可通过 列表初始化 从整数值直接初始化而无需显式转换:

  • 初始化方式为直接列表初始化
  • 初始化器列表仅包含单个元素
  • 枚举类型为有作用域枚举,或无作用域但具有固定底层类型
  • 转换过程不涉及窄化

这使得引入新型整数类型(例如 SafeInt )成为可能,即使在使用值传递结构体会产生性能损失的ABI上,这些新类型也能保持与底层整数类型相同的函数调用约定。

enum byte : unsigned char {}; // byte是新型整数类型;另见std::byte (C++17)
byte b{42};        // C++17起合法(直接列表初始化)
byte c = {42};     // 错误
byte d = byte{42}; // C++17起合法;值与b相同
byte e{-1};        // 错误
struct A { byte b; };
A a1 = {{42}};     // 错误(构造函数参数的拷贝列表初始化)
A a2 = {byte{42}}; // C++17起合法
void f(byte);
f({42}); // 错误(函数参数的拷贝列表初始化)
enum class Handle : std::uint32_t { Invalid = 0 };
Handle h{42}; // C++17起合法
(C++17 起)


using enum 声明

using enum using-enum-declarator ; (since C++20)
declarator - 一个(可能限定的) 标识符 简单模板标识符


declarator 必须命名一个非 依赖 的枚举类型。枚举声明通过仅限类型的普通 限定 非限定查找 找到,具体取决于 declarator 是否被限定。

enum E { x };
void f()
{
    int E;
    using enum E; // OK
}
using F = E;
using enum F; // OK
template<class T>
using EE = T;
void g()
{
    using enum EE<E>; // OK
}

using enum 声明引入命名枚举的枚举器名称,如同通过为每个枚举器使用 using 声明 。在类作用域中时, using enum 声明将命名枚举的枚举器作为成员添加到作用域,使其可用于成员查找。

enum class fruit { orange, apple };
struct S
{
    using enum fruit; // OK: 将 orange 和 apple 引入 S
};
void f()
{
    S s;
    s.orange;  // OK: 指代 fruit::orange
    S::orange; // OK: 指代 fruit::orange
}

两个引入同名枚举器的 using enum 声明会产生冲突。

enum class fruit { orange, apple };
enum class color { red, orange };
void f()
{
    using enum fruit;    // OK
    // using enum color; // 错误: color::orange 与 fruit::orange 冲突
}
(since C++20)

注释

无作用域枚举类型的值可以被 提升 转换 为整型:

enum color { red, yellow, green = 20, blue };
color col = red;
int n = blue; // n == 21

整数、浮点数和枚举类型的值可以通过使用 static_cast 转换为任何枚举类型。需要注意的是,转换后的值不一定等于为该枚举定义的任何命名枚举项:

enum access_t { read = 1, write = 2, exec = 4 }; // 枚举项:1, 2, 4 范围:0..7
access_t rwe = static_cast<access_t>(7);
assert((rwe & read) && (rwe & write) && (rwe & exec));
access_t x = static_cast<access_t>(8.0); // 自 CWG 1766 起为未定义行为
access_t y = static_cast<access_t>(8);   // 自 CWG 1766 起为未定义行为
enum foo { a = 0, b = UINT_MAX }; // 范围:[0, UINT_MAX]
foo x = foo(-1); // 自 CWG 1766 起为未定义行为,
                 // 即使 foo 的底层类型是 unsigned int
功能测试宏 标准 功能特性
__cpp_enumerator_attributes 201411L (C++17) 枚举器属性
__cpp_using_enum 201907L (C++20) using enum

关键词

enum struct class using

示例

#include <cstdint>
#include <iostream>
// 占用16位的枚举
enum smallenum: std::int16_t
{
    a,
    b,
    c
};
// color 可以是红色(值0)、黄色(值1)、绿色(值20)或蓝色(值21)
enum color
{
    red,
    yellow,
    green = 20,
    blue
};
// altitude 可以是 altitude::high 或 altitude::low
enum class altitude: char
{
    high = 'h',
    low = 'l', // 尾随逗号仅在 CWG 518 后允许
}; 
// 常量 d 为 0,常量 e 为 1,常量 f 为 3
enum
{
    d,
    e,
    f = e + 2
};
// 枚举类型(包括作用域和非作用域)可以拥有重载运算符
std::ostream& operator<<(std::ostream& os, color c)
{
    switch(c)
    {
        case red   : os << "red";    break;
        case yellow: os << "yellow"; break;
        case green : os << "green";  break;
        case blue  : os << "blue";   break;
        default    : os.setstate(std::ios_base::failbit);
    }
    return os;
}
std::ostream& operator<<(std::ostream& os, altitude al)
{
    return os << static_cast<char>(al);
}
// 作用域枚举(C++11)可以在早期 C++ 修订版中部分模拟:
enum struct E11 { x, y }; // 自 C++11 起
struct E98 { enum { x, y }; }; // 在 C++11 之前版本中可用
namespace N98 { enum { x, y }; } // 在 C++11 之前版本中可用
struct S98 { static const int x = 0, y = 1; }; // 在 C++11 之前版本中可用
void emu()
{
    std::cout << (static_cast<int>(E11::y) + E98::y + N98::y + S98::y) << '\n'; // 4
}
namespace cxx20
{
    enum class long_long_long_name { x, y };
    void using_enum_demo()
    {
        std::cout << "C++20 `using enum`: __cpp_using_enum == ";
        switch (auto rnd = []{return long_long_long_name::x;}; rnd())
        {
#if defined(__cpp_using_enum)
            using enum long_long_long_name;
            case x: std::cout << __cpp_using_enum << "; x\n"; break;
            case y: std::cout << __cpp_using_enum << "; y\n"; break;
#else
            case long_long_long_name::x: std::cout << "?; x\n"; break;
            case long_long_long_name::y: std::cout << "?; y\n"; break;
#endif
        }
    }
}
int main()
{
    color col = red;
    altitude a;
    a = altitude::low;
    std::cout << "col = " << col << '\n'
              << "a = "   << a   << '\n'
              << "f = "   << f   << '\n';
    cxx20::using_enum_demo();
}

可能的输出:

col = red
a = l
f = 3
C++20 `using enum`: __cpp_using_enum == 201907; x

缺陷报告

下列行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

缺陷报告 适用标准 发布时行为 正确行为
CWG 377 C++98 当不存在能表示所有枚举值的整型时行为未指定 此情况下枚举格式错误
CWG 518 C++98 枚举列表后不允许尾随逗号 允许
CWG 1514 C++11 具有固定底层类型的枚举重定义
可能被解析为类成员声明中的位域
始终解析为重定义
CWG 1638 C++11 不透明枚举声明的语法
禁止用于模板特化
允许嵌套名称说明符
CWG 1766 C++98 将超出范围的值转换到无固定底层类型的枚举
会产生未指定的结果
该行为未定义
CWG 1966 C++11 CWG 1514 的解决方案使条件表达式的 :
成为 enum-base 的一部分
仅将解决方案应用于
成员声明说明符
CWG 2156 C++11 枚举定义可通过using声明
定义枚举类型
禁止
CWG 2157 C++11 CWG 1966 的解决方案
未涵盖限定枚举名称
已涵盖
CWG 2530 C++98 枚举列表可包含多个
相同标识符的枚举项
禁止
CWG 2590 C++98 枚举的大小、值表示和对齐要求
不依赖于其底层类型
所有这些特性均与
底层类型完全相同
CWG 2621 C++20 using enum 声明中使用的
枚举名称查找不明确
已明确
CWG 2877 C++20 using enum 声明中使用的
枚举名称查找不是仅类型查找
改为仅类型查找

参考文献

  • C++23 标准 (ISO/IEC 14882:2024):
  • 9.7.1 枚举声明 [dcl.enum]
  • C++20 标准 (ISO/IEC 14882:2020):
  • 9.7.1 枚举声明 [dcl.enum]
  • C++17 标准 (ISO/IEC 14882:2017):
  • 10.2 枚举声明 [dcl.enum]
  • C++14 标准 (ISO/IEC 14882:2014):
  • 7.2 枚举声明 [dcl.enum]
  • C++11 标准 (ISO/IEC 14882:2011):
  • 7.2 枚举声明 [dcl.enum]
  • C++03 标准 (ISO/IEC 14882:2003):
  • 7.2 枚举声明 [dcl.enum]
  • C++98 标准 (ISO/IEC 14882:1998):
  • 7.2 枚举声明 [dcl.enum]

参见

(C++11)
检查类型是否为枚举类型
(类模板)
检查类型是否为有作用域枚举类型
(类模板)
获取给定枚举类型的基础整数类型
(类模板)
将枚举转换为其基础类型
(函数模板)
C 文档 关于 枚举