为什么std::make_error_code(std::errc)存在?相关设计逻辑疑问
核心设计逻辑拆解
1. std::make_error_code(std::errc)的作用是什么?
它的存在和错误码与错误条件的比较逻辑完全无关,是给你显式构造归属std::generic_category()的std::error_code实例用的。
典型使用场景是跨平台底层逻辑封装:比如你实现了跨平台文件打开接口,底层Windows返回ERROR_FILE_NOT_FOUND、Linux返回ENOENT,都可以统一生成std::make_error_code(std::errc::no_such_file_or_directory)作为对外返回的错误码,上层不需要感知平台差异就能拿到语义明确的通用错误码。
2. 为什么std::is_error_code_enum_v<std::errc>为false,却要提供这个函数?
这个 trait 为true的唯一作用是启用「枚举值到std::error_code的隐式转换」,标准刻意禁用这个隐式转换,核心是为了做语义隔离:std::errc的首要设计定位是跨平台错误条件,绝大多数场景下你应该用它来匹配任意错误码的语义(比如if (ec == std::errc::no_such_file_or_directory)),而不是直接用它构造错误码。如果开放隐式转换,很容易让开发者混淆错误码和错误条件的语义边界,写出不符合设计意图的代码,还可能和原生int型错误码的构造重载产生歧义。
提供显式的make_error_code函数,就是兼顾「确实需要构造通用错误码」的小众场景,同时通过显式调用的要求,避免语义被误用。
3. std::errc是不是同时属于两类枚举?
从功能上看确实是双重属性:它的枚举值和std::generic_category()的错误码、std::generic_category()的错误条件的整数值完全一一对应。但标准刻意把它的默认定位收敛到错误条件,就是为了引导开发者优先用它做语义匹配,而不是直接构造错误码,因此没有把is_error_code_enum_v设为true。
4. 自定义错误枚举应该怎么实现?
遵循和标准一致的设计原则即可:
- 如果你的枚举定位是纯错误条件,只用来做错误码的语义匹配,那只需要实现
make_error_condition函数即可,不需要提供make_error_code,也不要特化is_error_code_enum - 如果你的枚举确实有场景需要生成对应类别的
std::error_code,可以参考std::errc的设计,提供显式的make_error_code函数,但不要开放隐式转换 - 只有当你的枚举本身就是专属错误码枚举(比如对应你的自研组件的原生错误码,所有值都归属于你自定义的错误类别),才需要特化
std::is_error_code_enum_v<YourEnum> = true,启用隐式转换
内容的提问来源于stack exchange,提问作者Brad Spencer

