bam*_*bam 5 c++ enums undefined-behavior enum-class
我在一个项目中遇到了这种情况,其中我们有一些套接字通信,主要为流量控制交换字符。我们将这些字符转换为enum class : char一个开关。我想知道,如果另一端发送的字符不在我们的枚举类中,会发生什么。
我有这个 mwe:
enum class Foo : char {
UNKNOWN,
ENUM1 = 'A',
ENUM2 = 'B',
ENUM3 = 'C'
};
char bar1() {
return 'B';
}
char bar2() {
return 'D';
}
int main() {
switch((Foo)bar1()) {
case Foo::UNKNOWN:std::cout << "UNKNWON" << std::endl;break;
case Foo::ENUM1:std::cout << "ENUM1" << std::endl;break;
case Foo::ENUM2:std::cout << "ENUM2" << std::endl;break;
case Foo::ENUM3:std::cout << "ENUM3" << std::endl;break;
default:std::cout << "DEFAULT" << std::endl;break;
}
switch((Foo)bar2()) {
case Foo::UNKNOWN:std::cout << "UNKNWON" << std::endl;break;
case Foo::ENUM1:std::cout << "ENUM1" << std::endl;break;
case Foo::ENUM2:std::cout << "ENUM2" << std::endl;break;
case Foo::ENUM3:std::cout << "ENUM3" << std::endl;break;
default:std::cout << "DEFAULT" << std::endl;break;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在这个例子中,我有一个enum class : char未指定的条目和三个字符分配的条目。当我运行它时,我收到的输出是
ENUM2
DEFAULT
Run Code Online (Sandbox Code Playgroud)
这似乎完美无缺,因为未定义的示例只是跳转到默认情况。然而,这是“保存要做”吗?是否有一些我现在可能看不到的陷阱或其他并发症?
这是完全安全的,因为:
enum class是一个作用域枚举;: char;char;这里的 C++17 标准引用与上述语句相对应:
[dcl.enum]/2: (...) 枚举键
enum class和enum struct在语义上是等价的;使用其中之一声明的枚举类型是作用域枚举,并且其枚举器是作用域枚举器。[dcl.enum]/5: (...) 每个枚举也有一个基础类型。可以使用枚举基显式指定基础类型。(...) 在这两种情况下,基础类型被称为是 固定的。(...)
[dcl.enum]/8: 对于基础类型固定的枚举, 枚举的值就是基础类型的值。(...)
[expr.static.cast]/10 整型或枚举类型的值可以显式转换为完全枚举类型。如果枚举类型具有固定的基础类型,则值首先通过整型转换(如有必要)转换为该类型,然后再转换为枚举类型。[expr.cast]/4 由 const_cast、static_cast、static_cast 后跟 const_cast、reinterpret_cast、reinterpret_cast 后跟 const_cast 执行的转换可以使用显式类型转换的强制转换表示法来执行。(...) 如果转换可以用上面列出的多种方式解释,则使用列表中第一个出现的解释 (...)
如果基础类型不固定,结论就会不同。在这种情况下, [dcl.enum]/8 的剩余部分将适用:它或多或少表示,如果您不在枚举的最小和最大枚举器内,则您不确定该值可以是代表。
另请参阅问题是否允许枚举具有未列出的值?,它更通用(C++ 和 C),但不使用作用域枚举或指定的基础类型。
这里是一个使用未定义枚举器的枚举值的代码片段:
switch((Foo)bar2()) {
case Foo::UNKNOWN: std::cout << "UNKNWON" << std::endl;break;
case Foo::ENUM1: std::cout << "ENUM1" << std::endl;break;
case Foo::ENUM2: std::cout << "ENUM2" << std::endl;break;
case Foo::ENUM3: std::cout << "ENUM3" << std::endl;break;
case static_cast<Foo>('D'): std::cout << "ENUM-SPECIAL-D" << std::endl;break;
default: std::cout << "DEFAULT" << std::endl;break;
}
Run Code Online (Sandbox Code Playgroud)