如果我有一个switch语句显式地处理所有的enum case,编译器是否可以优化掉默认的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;
}
}
Cpp Reference说关于枚举(强调我的):
整数、浮点和枚举类型的值可以通过static_cast或显式转换转换为任何枚举类型。如果基础类型不固定并且源值超出范围,则行为未定义。(源值,如果是浮点型,则转换为枚举的基础类型,如果最小比特字段的大小足以容纳目的枚举型别的所有枚举值,则在范围内)。否则,结果会与隐含转换为基础型别的结果相同。
请注意,在这种转换之后的值可能不一定相等为枚举型别定义的任何具名枚举值。
这表示允许优化上例中的default语句,因为底层类型不是固定的,并且指定了每个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
这清楚地表明,“range”与命名的枚举器值有关,而不是任何整数类型的全宽。当然,假设cppreference示例是ISO标准的准确反映,这意味着显式或隐式转换不能合法地生成具有未考虑的值的enum
对象。
2条答案
按热度按时间s3fp2yjn1#
是的,我想理论上是可以的。没有标准认可的方法来达到
default
。正如在cppreference页面上提到的,标准在[dcl.enum]/8中本质上说,没有固定底层类型的枚举的可能值是0到2的最小幂,不包括2的最小幂,能够适合所有声明的枚举值。通过
static_cast
从底层类型强制转换到这个范围之外的值是显式未定义的行为。关于
memcpy
/std::bit_cast
从底层类型到枚举可能还有一些争论的空间,但我认为这也行不通。链接的标准段落似乎专门定义了枚举的值,因此对象表示不可能表示任何额外的值(假设它们是枚举的有效对象表示)。对于枚举所具有的值,该标准目前甚至不能保证与底层类型相同的表示(尽管这可能是一个缺陷)。
然而,它与C不兼容,因为C允许底层类型的所有值。由于最小的底层类型是
char
,它至少有8位宽,所以在C中总是可以到达default
。因此,我不希望任何编译器在没有参数范围的额外知识的情况下认为
default
不可达。具有固定基础型别的枚举型别(包含所有
enum class
)会以不同方式指定,并保证支援基础型别的所有值,如同C语言中所有枚举型别的情况。lnlaulya2#
一些编译器非常积极地假设未定义的行为不会发生(因此优化了代码)。但是在C中,创建一个不匹配任何大小写的枚举值太容易了。而且你展示的代码明确地打算检测不正确的值。
我从来没有见过或听说过编译器优化这种情况。不同的是,在像Swift这样的语言中,设置一个枚举到一个没有作为一个情况提到的值是非常非常困难的。