C++17中跨enum class花括号初始化合法而赋值初始化报错原因
强类型枚举跨类型初始化行为差异原因
两种初始化语法的标准规则差异
C++17标准对两类初始化写法的类型校验规则存在明确区别:
FOO value2 = BAR::VALUE;属于拷贝初始化,要求初始化表达式必须支持隐式转换到目标类型。enum class(强类型枚举)的核心设计目标就是阻断跨枚举类型、枚举到整数的默认隐式转换,因此该写法编译报错是完全符合标准的正确行为。FOO value1{BAR::VALUE};属于直接列表初始化,按照C++17标准规定,强类型枚举的列表初始化同样不允许使用其他枚举类型的值作为初始化器,符合规范的编译器实现下也应当抛出编译错误。
Clang 12/13/14版本的实现缺陷
大括号初始化跨枚举类型被错误放行,是上述三个Clang版本的已知编译器bug,该问题在Clang 15版本已完成修复,修复后该写法会和拷贝初始化一致触发编译报错。
bug的触发逻辑为:
- Clang实现强类型枚举直接列表初始化逻辑时,遗漏了对初始化器的原始枚举类型校验步骤
- 编译器错误地将任意枚举类型的初始化值,提前转换为其对应的底层整数类型值,直接走入「整数值初始化枚举」的校验分支
- 示例代码中
FOO和BAR的默认底层类型均为int,BAR::VALUE对应的整数值为0,落在FOO的合法取值范围内,因此被错误放行。
验证方法:给两个枚举指定不同的底层类型,例如编写
enum class FOO : char { VALUE };、enum class BAR : long { VALUE };,即使在Clang 12/13/14版本下,FOO value1{BAR::VALUE};也会正常编译报错——因为底层类型不匹配,无法走入错误的整数校验分支。
强类型枚举具备完整类型安全、默认禁止跨枚举隐式转换的认知完全符合C++标准设计,异常放行的现象不属于标准允许的行为,仅为旧版本编译器的实现缺陷。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

