duo点 发表于 2024-10-21 15:40

从 C++17、C++20 到 C++23,不断进阶的枚举类!

C++语言的演进不断带来强大的新特性,提升了代码的安全性、可读性和可维护性。在这些改进中,我们见证了从 C++17、C++20 到 C++23 中对 enum class 功能的修改和扩展。在这篇文章中,我们将探讨这些进展,重点介绍 C++17 中的初始化改进、C++20 中引入的 using enum 关键字,以及 C++23 中的 std::to_underlying 实用工具。1、enum class 简介在深入了解这些改进之前,让我们先简要回顾一下 enum class 是什么。enum class(限定作用域的枚举)提供了一种类型安全的方式来定义一组命名常量。与传统的(无作用域)枚举不同,enum class 不会隐式转换为整数或其他类型,从而防止了意外的误用。下面是一个基本示例:#include <iostream>
enum class Color {    Red,    Green,    Blue};
int main() {      Color color = Color::Red;
    if (color == Color::Red)      std::cout << "The color is red.\n";
    color = Color::Blue;
    if (color == Color::Blue)      std::cout << "The color is blue.\n";
    // std::cout << color; // error, no matching << operator    // int i = color;      // error: cannot convert}注意,在 main 函数的末尾有两行代码。由于没有隐式转换为整数类型,因此会出现编译器错误。作为对比,下面是一个类似的例子,但使用的是无作用域枚举:#include <iostream>
enum Color {    Red,    Green,    Blue};
int main() {      Color color = Red;
    if (color == Red)      std::cout << "The color is red.\n";
    color = Blue;
    if (color == Blue)      std::cout << "The color is blue.\n";
    std::cout << color; // fine, prints integer value!    int i = color;      // fine, can convert...}简而言之,enum class 为所有枚举值提供了单独的作用域,同时也加强了类型安全。没有隐式的整数转换,这样你就能更好地控制设计。以上的基础部分很简单,接下来让我们来看看最新 C++ 版本中的一些实用改进。

duo点 发表于 2024-10-21 15:40

2、C++17:使用大括号初始化基础类型有时候,enum class 可能显得过于限制,某些情况下的转换可能会很方便。在 C++17 中,P0138 提案被接受,以下是其中的一个示例:enum class Handle : uint32_t { Invalid = 0 }; Handle h { 42 }; // OK简而言之,当你使用 enum class 来定义强类型时,允许从基础类型进行初始化而不产生任何错误——这在 C++17 之前是无法实现的。这个变化仍能确保枚举仍然是安全的,因为它们只能用于统一/大括号初始化。请看看下面的代码:#include <iostream>
enum class Handle : uint32_t { Invalid = 0 };
void process(Handle h) {
}
int main() {      Handle h { 42 }; // OK
    // process({10}); // error    process(Handle{10});}你不能直接将 {10} 作为 process 函数的参数,仍需要明确指定类型。在 C++14 中,你可以使用 process(static_cast<Handle>(10));——如你所见,C++17 版本的改进要好得多。

duo点 发表于 2024-10-21 15:40

3、C++20:使用 using enumC++20 引入了 using enum 语法,这个特性允许你将一个枚举的所有枚举值引入当前作用域,同时不失去作用域枚举的优点。请看下面的示例:enum class ComputeStatus {    Ok,    Error,    FileError,    NotEnoughMemory,    TimeExceeded,    Unknown};在早期的 C++ 版本中,使用这些枚举值时,需要使用枚举类的名称进行限定:ComputeStatus s = ComputeStatus::NotEnoughMemory;C++20 通过 using enum 声明简化了这一点:int main() {    using enum ComputeStatus;    ComputeStatus s = NotEnoughMemory;}上面的简单代码可能没什么实际意义,但看看下面这个例子:int main() {      ComputeStatus s = ComputeStatus::Ok;    switch (s) {      case ComputeStatus::Ok:             std::cout << "ok"; break;      case ComputeStatus::Error:             std::cout << "Error"; break;      case ComputeStatus::FileError:             std::cout << "FileError"; break;      case ComputeStatus::NotEnoughMemory:             std::cout << "NotEnoughMemory"; break;      case ComputeStatus::TimeExceeded:             std::cout << "Time..."; break;      default: std::cout << "unknown...";    }}我们可以将其转换为如下形式:int main() {      ComputeStatus s = ComputeStatus::Ok;    switch (s) {      using enum ComputeStatus;// << <<      case Ok:             std::cout << "ok"; break;      case Error:             std::cout << "Error"; break;      case FileError:             std::cout << "FileError"; break;      case NotEnoughMemory:             std::cout << "NotEnoughMemory"; break;      case TimeExceeded:             std::cout << "Time..."; break;      default: std::cout << "unknown...";    }}或者,也可以看看下面这个例子:struct ComputeEngine {    enum class ComputeStatus {      Ok,      Error,      FileError,      NotEnoughMemory,      TimeExceeded,      Unknown    };    using enum ComputeStatus;};
int main() {      ComputeEngine::ComputeStatus s = ComputeEngine::Ok;}你可以将所有枚举值引入 ComputeEngine 的作用域中,同时享受 enum class 带来的类型安全特性。C++20 的这一改进使代码更加简洁,并减少了冗余,尤其是在某个作用域内频繁使用多个枚举值的情况下。它提供了一种更加流畅和可读的方式,同时不牺牲作用域枚举所提供的类型安全性。

duo点 发表于 2024-10-21 15:41

4、C++23:std::to_underlyingC++23 通过引入 std::to_underlying 进一步增强了 enum class 的可用性,这个实用函数可以将枚举值转换为其基础的整型类型,这个特性解决了将枚举值转换为整数以用于存储、比较或与其他期望整型的 API 交互的常见需求。这个想法最早出现在 Scott Meyers 的经典著作《Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14》一书中。终于在 C++23 中,我们可以享受到这个被标准化的功能。在 C++23 之前,将枚举转换为其基础类型需要显式的类型转换:enum class Permissions : uint8_t {    Execute = 1,Write = 2,    Read = 4};
uint8_t value = static_cast<uint8_t>(Permissions::Read);而有了 std::to_underlying,这个转换变得更加直接和清晰:#include <type_traits>
int main() {    Permissions p = Permissions::Read;    auto value = std::to_underlying(p); // C++23}std::to_underlying 函数提高了代码的可读性,并减少了与类型转换相关的样板代码。它还明确了意图,使得人们能一目了然地知道这是在获取枚举的基础值。
5、未来的改进接下来,一个即将到来的重要更新可能是支持 C++26 反射——可查看提案 P2996(虽然这个提案还未被接受,但预计很快就会获得批准)。反射带来了许多激动人心的可能性,比如将枚举转换为字符串的能力。请看提案中的这个例子:template <typename E>requires std::is_enum_v<E>constexpr std::string enum_to_string(E value) {template for (constexpr auto e : std::meta::enumerators_of(^E)) {    if (value == [:e:]) {      return std::string(std::meta::name_of(e));    }}return "<unnamed>";}
enum Color { red, green, blue };static_assert(enum_to_string(Color::red) == "red");static_assert(enum_to_string(Color(42)) == "<unnamed>");声明:本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。

yangxiaor520 发表于 2024-10-22 07:56

C++现在是固定3年一升级啊

呐咯密密 发表于 2024-10-22 10:29

格式好乱啊

tpgf 发表于 2024-10-28 15:10

C++ 枚举类(enum class)是一种强类型枚举,它在 C++11 标准中引入,旨在解决传统枚举(enum)的一些缺陷

szt1993 发表于 2024-11-24 22:36

使用大括号初始化基础类型有时候,enum class 可能显得过于限制,某些情况下的转换可能会很方便。

LOVEEVER 发表于 2024-11-25 23:32

没有隐式的整数转换,能更好地控制设计

中国龙芯CDX 发表于 2024-11-27 09:49

enum class(限定作用域的枚举)提供了一种类型安全的方式来定义一组命名常量。

小夏天的大西瓜 发表于 2024-11-27 23:45

enum class 是什么?

小小蚂蚁举千斤 发表于 2024-11-29 21:51

enum class 不会隐式转换为整数或其他类型,从而防止了意外的误用。

Henryko 发表于 2024-11-30 08:06

用不上这么先进的版本啊

作业粉碎机 发表于 2025-7-9 16:54

确实,C++的每次更新都带来了许多实用的新特性,枚举类的改进让代码更加健壮。

绝影孤狼 发表于 2025-7-10 08:04

这篇文章提供了对 C++ 枚举类演进的很好概述,特别是对于类型安全和代码可维护性方面的改进。期待看到更多关于 std::to_underlying 的实际应用案例。

幻想收藏家 发表于 2025-7-11 11:33

C++17的enum class改进确实让代码更加安全和清晰了,特别是在初始化和类型安全方面。

彩虹捕手 发表于 2025-7-12 11:12

这篇文章很好地概述了枚举类在C++中的演变,特别是C++17和C++20中的改进。期待C++23带来更多的惊喜!

治愈糖果屋 发表于 2025-7-12 20:47

这篇文章提供了一个很好的概述,让我们对枚举类在C++中的演变有了清晰的认识。期待看到更多关于C++23中std::to_underlying的讨论。
页: [1]
查看完整版本: 从 C++17、C++20 到 C++23,不断进阶的枚举类!