网易首页 > 网易号 > 正文 申请入驻

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

0
分享至

随着 C++ 标准的不断演进,枚举类型(enum class)作为一种重要的数据结构,在 C++ 社区中扮演着越来越重要的角色。从 C++17 到 C++23,我们见证了枚举类型的多项改进和完善,这些变化不仅增强了语言本身的表达能力,也为开发者提供了更强大、更安全的编程工具。

原文链接:https://www.cppstories.com/2024/enum-improvements/

声明:未经允许,禁止转载。

作 者 | Bartłomiej Filipek

翻译 | 郑丽媛

C++语言的演进不断带来强大的新特性,提升了代码的安全性、可读性和可维护性。在这些改进中,我们见证了从 C++17、C++20 到 C++23 中对 enum class 功能的修改和扩展。在这篇文章中,我们将探讨这些进展,重点介绍 C++17 中的初始化改进、C++20 中引入的 using enum 关键字,以及 C++23 中的 std::to_underlying 实用工具。

enum class 简介

在深入了解这些改进之前,让我们先简要回顾一下 enum class 是什么。enum class(限定作用域的枚举)提供了一种类型安全的方式来定义一组命名常量。与传统的(无作用域)枚举不同,enum class 不会隐式转换为整数或其他类型,从而防止了意外的误用。下面是一个基本示例:

#include

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

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++ 版本中的一些实用改进。

C++17:使用大括号初始化基础类型

有时候,enum class 可能显得过于限制,某些情况下的转换可能会很方便。

在 C++17 中,P0138 提案被接受,以下是其中的一个示例:

enum class Handle : uint32_t { Invalid = 0 };
Handle h { 42 }; // OK

简而言之,当你使用 enum class 来定义强类型时,允许从基础类型进行初始化而不产生任何错误——这在 C++17 之前是无法实现的。

这个变化仍能确保枚举仍然是安全的,因为它们只能用于统一/大括号初始化。请看看下面的代码:

#include

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 (10));——如你所见,C++17 版本的改进要好得多。

C++20:使用 using enum

C++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 的这一改进使代码更加简洁,并减少了冗余,尤其是在某个作用域内频繁使用多个枚举值的情况下。它提供了一种更加流畅和可读的方式,同时不牺牲作用域枚举所提供的类型安全性。

C++23:std::to_underlying

C++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

 (Permissions::Read); 

而有了 std::to_underlying,这个转换变得更加直接和清晰:

#include

int main() {
Permissions p = Permissions::Read;
auto value = std::to_underlying(p); // C++23
}

std::to_underlying 函数提高了代码的可读性,并减少了与类型转换相关的样板代码。它还明确了意图,使得人们能一目了然地知道这是在获取枚举的基础值。

未来的改进

接下来,一个即将到来的重要更新可能是支持 C++26 反射——可查看提案 P2996(虽然这个提案还未被接受,但预计很快就会获得批准)。反射带来了许多激动人心的可能性,比如将枚举转换为字符串的能力。

请看提案中的这个例子:

template 

 requires std::is_enum_v 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 " " ; } enum Color { red, green, blue }; static_assert(enum_to_string(Color::red) == "red"); static_assert(enum_to_string(Color(42)) == " " ); 

当然,你也可以不必等到 C++26,依赖第三方库就能体验这个功能,如 Neargye/magic_enum @GitHub。

能学习到新知识、产生共鸣,解答久困于心的困惑,这是《新程序员》的核心价值。欢迎扫描下方二维码订阅纸书和电子书。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
领先15分被翻盘!山东输得真窝囊,老将空砍30+12,彭华倩25+6

领先15分被翻盘!山东输得真窝囊,老将空砍30+12,彭华倩25+6

萌兰聊个球
2025-11-14 15:46:01
李诞吐槽徐志胜家 “太埋汰”,连说4遍嫌脏!志胜在线辟谣:我在家连脏话都不说,就知道有多干净

李诞吐槽徐志胜家 “太埋汰”,连说4遍嫌脏!志胜在线辟谣:我在家连脏话都不说,就知道有多干净

鲁中晨报
2025-11-14 10:15:04
六国外援候命,高市早苗通知全球,对华开第二枪,我军上硬菜

六国外援候命,高市早苗通知全球,对华开第二枪,我军上硬菜

玉涵简书
2025-11-14 14:53:12
“狗咬人致命案”新进展:9人砸窗破门误伤自己人,导致妹夫重伤

“狗咬人致命案”新进展:9人砸窗破门误伤自己人,导致妹夫重伤

汉史趣闻
2025-11-13 21:05:13
42岁刘翔近况曝光,长期在国外旅游,靠终身合同吸金,远离喷子!

42岁刘翔近况曝光,长期在国外旅游,靠终身合同吸金,远离喷子!

姩姩有娱
2025-11-10 17:49:18
曾医生的父母这几天火遍全国,因为他们不怕认错,敢于和勇于认错

曾医生的父母这几天火遍全国,因为他们不怕认错,敢于和勇于认错

诗意世界
2025-11-13 15:54:30
柬埔寨“太子集团”首度发声:否认从事非法活动,辩称12.7万枚比特币4年多前被黑客窃取

柬埔寨“太子集团”首度发声:否认从事非法活动,辩称12.7万枚比特币4年多前被黑客窃取

红星新闻
2025-11-13 19:47:43
对华强硬的80后女政客,正式就任联合国大会主席:联合国完蛋了

对华强硬的80后女政客,正式就任联合国大会主席:联合国完蛋了

历史有些冷
2025-11-13 16:30:03
神舟二十号航天员返回几点开始直播?返回全程直播在哪看?

神舟二十号航天员返回几点开始直播?返回全程直播在哪看?

楠楠自语
2025-11-14 13:43:35
变天!库班回归掌权!欧文浓眉克莱全卖!

变天!库班回归掌权!欧文浓眉克莱全卖!

柚子说球
2025-11-14 10:29:17
“冷美人”退赛!荒野求生第37天晕倒,复查不合格

“冷美人”退赛!荒野求生第37天晕倒,复查不合格

九方鱼论
2025-11-14 14:44:36
不回头了?富士康决定转移3000亿产能,外媒:郭台铭把饭碗端走了

不回头了?富士康决定转移3000亿产能,外媒:郭台铭把饭碗端走了

混沌录
2025-10-22 21:54:06
祖院长事件持续发酵,眼科医生只是其中之一,前任的报复稳准狠!

祖院长事件持续发酵,眼科医生只是其中之一,前任的报复稳准狠!

易会科普
2025-11-13 16:32:56
央视怒批,国务院点名封杀!这几位蒙骗老百姓的大网红,彻底凉凉

央视怒批,国务院点名封杀!这几位蒙骗老百姓的大网红,彻底凉凉

大鱼简科
2025-09-02 19:34:00
驾驶证不再是12分?车辆将不再年检?交警:有车没车都需要了解下

驾驶证不再是12分?车辆将不再年检?交警:有车没车都需要了解下

李博世财经
2025-11-14 14:23:08
呼和浩特百亩向日葵被70多人哄抢?街道办:疑似缺少值守,被零星采摘发展到群体采摘

呼和浩特百亩向日葵被70多人哄抢?街道办:疑似缺少值守,被零星采摘发展到群体采摘

潇湘晨报
2025-11-14 12:14:15
网友喊“斩杀”“闽南狼”,吴思瑶暴怒,卢秀燕更愤怒,蓝绿合流

网友喊“斩杀”“闽南狼”,吴思瑶暴怒,卢秀燕更愤怒,蓝绿合流

阿天爱旅行
2025-11-14 15:28:15
2909亿身家有何用?97岁的李嘉诚无力回天,两个60岁儿子已成心病

2909亿身家有何用?97岁的李嘉诚无力回天,两个60岁儿子已成心病

春秋论娱
2025-11-14 07:27:51
库里与安德玛分手!他将成为球鞋领域自由球员 库里品牌独立运营

库里与安德玛分手!他将成为球鞋领域自由球员 库里品牌独立运营

罗说NBA
2025-11-14 06:49:52
著名神经病学家蒋雨平因病逝世,享年83岁

著名神经病学家蒋雨平因病逝世,享年83岁

澎湃新闻
2025-11-13 17:28:37
2025-11-14 17:35:00
CSDN incentive-icons
CSDN
成就一亿技术人
26099文章数 242181关注度
往期回顾 全部

科技要闻

京东“失去的五年”后,找到新增长了吗?

头条要闻

29岁程序员疑被骗至东南亚 出租屋内留下一张神秘纸条

头条要闻

29岁程序员疑被骗至东南亚 出租屋内留下一张神秘纸条

体育要闻

40岁C罗肘击染红 离场时怒骂对手主帅

娱乐要闻

《国色天香》编剧发长文质疑古二?

财经要闻

统计局:前10月房地产开发投资下降14.7%

汽车要闻

小鹏X9超级增程动态评测全网首发 高速实测车内65分贝

态度原创

本地
时尚
手机
数码
公开课

本地新闻

云游安徽 | 江声浩荡阅千年,文脉相承看芜湖

秋天穿衣真的一点都不难!从这些穿搭中收获灵感,舒适又耐看

手机要闻

天玑霸榜!安兔兔10月安卓次旗舰手机性能榜出炉

数码要闻

Steam Frame发布后,Valve宣告Index VR头显已停产

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版