可以针对枚举优化 switch 默认语句吗

das*_*t99 9 c++ enums compiler-optimization language-lawyer c++20

如果我有一个显式处理所有枚举情况的 switch 语句,是否允许编译器优化默认的 case 语句?

enum MyEnum {
 ZERO = 0,
 ONE = 1,
 TWO = 2,
 THREE = 3,
};

bool foo(MyEnum e) {
 switch(e) {
 case ZERO:
 case ONE:
 case TWO:
 case THREE:
  return true;
 default:    // Could a compiler optimise this away?
  return false;
 }
}
Run Code Online (Sandbox Code Playgroud)

Cpp Reference说关于枚举(强调我的):

整数、浮点和枚举类型的值可以通过 static_cast 或显式强制转换转换为任何枚举类型。如果基础类型不固定且源值超出范围,则行为未定义。(如果源值转换为枚举的基础类型(如果是浮点型),则如果它适合足够大以容纳目标枚举的所有枚举器的最小位字段,则该源值在范围内。)否则,结果与隐式转换为基础类型的结果。

请注意,此类转换后的值不一定等于为枚举定义的任何命名枚举器。

这表明可以优化上面示例中的默认语句,因为基础类型不固定并且指定了每个 2 位值(尽管您可能需要包含负值到 -4)。

但是,尚不清楚这是否也适用于固定类型枚举或枚举类。


实际上,GCC、clang 和 MSVC 并不假定枚举是定义的值之一。(Godbolt启用了全面优化,例如-O3 -std=c++20。)

这是错过的优化,还是标准说如果实现选择int作为基础类型(即使您没有指定)那么任何int值都是合法的?

CppReference 在引用的段落下显示了一个示例:

enum access_t { read = 1, write = 2, exec = 4 }; 
 // enumerators: 1, 2, 4 range: 0..7
access_t y = static_cast<access_t>(8);   // undefined behavior since CWG1766
Run Code Online (Sandbox Code Playgroud)

这清楚地表明“范围”与命名的枚举值相关,而不是任何整数类型的完整宽度。当然,假设 cppreference 示例准确反映了 ISO 标准,这意味着显式或隐式转换不能合法地生成enum具有未考虑的值的对象。

use*_*522 3

是的,我想理论上他们可以。没有标准认可的方法可以达到default.

正如 cppreference 页面上提到的,该标准本质上在[dcl.enum]/8中表示,没有固定基础类型的枚举的可能值为零到不包括能够适合所有声明的枚举器值的 2 的最小幂。从基础类型转换为该范围之外的值static_cast是显式未定义的行为。

从底层类型到枚举可能有一些争论的余地memcpystd::bit_cast但我认为这也行不通。链接的标准段落似乎专门定义了枚举的值,因此对象表示不可能表示任何其他值(假设它们一开始就是枚举的有效对象表示)。

该标准当前甚至不保证枚举所具有的值与基础类型具有相同的表示形式(尽管这可能是一个缺陷)。

但是,它与 C 不兼容,因为 C 中允许基础类型的所有值。由于最小可能的底层类型char至少有 8 位宽,因此始终可以到达defaultC 中的。

default因此,我不希望任何编译器在没有额外了解参数范围的情况下考虑无法访问的内容。

具有固定基础类型(包括 all enum class)的枚举以不同方式指定,并保证支持基础类型的所有值,就像 C 中所有枚举的情况一样。