1、介绍

1.1 enum

传统枚举enum在C++98 标准中就存在,是一种未限域枚举:

enum Color { 
		RED, 
		GREEN, 
		BLUE
 		};

1.2 enum class

c++11引入限域枚举:

enum class Color {
    RED,    
    GREEN,  
    BLUE    
};

二、区别

2.1 命名空间污染问题

当我们声明一个 enum 时,其中的枚举常量会泄漏到外层作用域,这意味着在该作用域内不能再出现与枚举常量同名的标识符,否则会导致编译错误。

enum Color { RED, GREEN };
enum Light { RED }; // 错误!重定义

即使两个枚举被定义在不同的头文件中,只要被一个文件同时引用,也会出现冲突,甚至枚举常量会和函数名出现冲突:

enum Color { RED, GREEN, BLUE };
void RED() {// 编译错误:与枚举值冲突
    int aaa = 255; 
}

C++11引入的enum class彻底解决了上述问题:

enum class Color { RED, GREEN };
enum class Light { RED}; // 正确
Color c = Color::RED; // 必须使用作用域限定

enum class对枚举常量的作用域进行了限定,枚举常量仅在其所属的枚举类型作用域内有效,但必须通过作用域限定符::才能访问。

2.2 类型安全问题

enum 类型不安全,它允许枚举常量隐式转换为整数类型,同时也允许不同枚举类型的常量之间进行比较,这可能会导致一些隐藏的 bug。

// enum的隐式转换问题
enum A{
   RED, 
   GREEN, 
   BLUE
};
enum B{
    APPLE, 
    BANANA
};
// 枚举常量隐式转换为int
int value= RED; // 正确,low被隐式转换为0
// 枚举常量与其他数值比较
if (GREEN== 1) { // 正确,medium隐式转换为1后比较
    // ...
}
// 参数传递错误不会被编译器检测
void process(A a, B b) {
    // ...
}
int main() {
    A a = BLUE;
    B b = BANANA;
    process(b, a); // 错误地交换了参数,但编译器不会报错,因为存在隐式转换
    return 0;
}

enum class 类型安全,不允许枚举常量的隐式转换,不同枚举类型之间也不能进行比较,必须通过显式转换才能将枚举常量转换为其他类型:

enum class A{
   RED, 
   GREEN, 
   BLUE
};
enum class B{
    APPLE, 
    BANANA
};
// 枚举常量不能隐式转换为int
int value= A::RED; // 错误
int value = static_cast<int>(A::low); // 正确
// 枚举常量与其他数值比较
if (A::GREEN== 1) { // 错误
    // ...
}
// 不同枚举类型之间不能比较
if (A::GREEN== B::BANANA) { // 编译错误
    // ...
}

void process(A a, B b) {
    // ...
}
int main() {
    A a = A::BLUE;
    B b = B::BANANA;
    process(b, a); // 编译错误
    return 0;
}

2.3 前置声明支持

enum 不支持前置声明:

enum Status; // 编译错误,必须同时定义
// 必须像这样声明并定义
enum Status {
    success,
    failure
};

enum class 支持前置声明:

// enum class支持前置声明
enum class Status; // 正确,前置声明
// 在其他地方进行定义
enum class Status {
    success,
    failure
};

同时 enum class支持底层类型指定:

// C++11中,显式指定底层类型的enum支持前置声明
enum Status : int; // 正确,前置声明并指定底层类型为int
// 定义enum
enum Status : int {
    success,
    failure
};

3.高级用法

3.1 安全的转换函数

enum class Status : int {
    Success = 0,
    Failed = -1,
    Timeout = -2
};

// 封装转换函数
constexpr int to_int(Status s) {
    // 强转底层类型,C++11 起支持 constexpr
    return static_cast<std::underlying_type_t<Status>>(s);
}
int main() {
    Status s = Status::Success;
    // 显式转换(正确)
    int val = to_int(s); // val = 0
    return 0;
}

3.2枚举值遍历

#include <array>
#include <iostream>

enum class Color : uint8_t {
    Red,
    Green,
    Blue,
    Yellow
};
// 定义枚举值列表(C++11 起可用 std::array)
constexpr std::array<Color, 4> ColorValues = {
    Color::Red, Color::Green, Color::Blue, Color::Yellow
};
// 遍历函数
void print_all_colors() {
    for (auto c : ColorValues) {
        int val = static_cast<uint8_t>(c);
        std::cout << "Color: " << val << std::endl;
    }
}
// C++17 增强:constexpr 遍历
template <typename Enum, auto& Values>
constexpr void for_each_enum(auto&& callback) {
    for (const auto& e : Values) {
        callback(e);
    }
}

int main() {
    print_all_colors();    
    // C++17 遍历(编译期安全)
    for_each_enum<Color, ColorValues>([](Color c) {
        std::cout << "Enum value: " << static_cast<int>(c) << std::endl;
    });
    return 0;
}

3.3作为容器键

enum class HttpStatus : uint16_t {
    OK = 200,
    CREATED = 201,
    BAD_REQUEST = 400,
    NOT_FOUND = 404,
    INTERNAL_ERROR = 500
};

// 作为map键
std::map<HttpStatus, std::string> status_messages = {
    {HttpStatus::OK, "OK"},
    {HttpStatus::CREATED, "Created"},
    {HttpStatus::NOT_FOUND, "Not Found"},
    {HttpStatus::INTERNAL_ERROR, "Internal Server Error"}
};

// 有序遍历
for (const auto& [status, message] : status_messages) {
    std::cout << static_cast<int>(status) << ": " << message << std::endl;
}

3.4运算符重载

// 为枚举类定义运算符
enum class FilePermission : uint8_t {
    NONE  = 0b000,
    READ  = 0b001,
    WRITE = 0b010,
    EXEC  = 0b100,
    ALL   = READ | WRITE | EXEC
};
// 位运算运算符
inline FilePermission operator|(FilePermission lhs, FilePermission rhs) {
    return static_cast<FilePermission>(
        static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}

inline FilePermission operator&(FilePermission lhs, FilePermission rhs) {
    return static_cast<FilePermission>(
        static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}

inline FilePermission operator^(FilePermission lhs, FilePermission rhs) {
    return static_cast<FilePermission>(
        static_cast<uint8_t>(lhs) ^ static_cast<uint8_t>(rhs));
}

inline FilePermission operator~(FilePermission p) {
    return static_cast<FilePermission>(~static_cast<uint8_t>(p));
}
inline FilePermission& operator|=(FilePermission& lhs, FilePermission rhs) {
    lhs = lhs | rhs;
    return lhs;
}

// 使用方法
FilePermission perms = FilePermission::READ | FilePermission::WRITE;
if ((perms & FilePermission::READ) != FilePermission::NONE) {
    // 有读权限
}

在这里插入图片描述

更多推荐