我在一个项目中遇到这种情况,我们有一些套接字通信,主要交换字符以进行流控制。
我们在开关中将这些字符转换为---
...
aliases: [
hello,
world,
inserted
]
---
...
。
我想知道,如果另一端发送的字符不在我们的枚举类中,会发生什么情况。
我有这个mwe:
enum class : char
在此示例中,我有一个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;
}
,其中包含一个未指定的条目和三个字符分配的条目。当我运行它时,我收到的输出是
enum class : char
这似乎可以正常工作,因为未定义的示例只是跳转到默认情况。但是,这是“省事”吗? 有一些我现在可能看不到的陷阱或其他并发症吗?
答案 0 :(得分:2)
这是完全安全的,因为:
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的其余部分将适用:它或多或少地表明,如果您不在枚举的最小和最大枚举器之内,则不确定该值是否可以代表。
另请参见问题Is it allowed for an enum to have an unlisted value?,该问题较为笼统(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;
}
答案 1 :(得分:1)
这并不完全安全。我发现C ++ Standard [expr.static.cast]第10段指出以下内容:
整数或枚举类型的值可以显式转换为枚举类型。如果原始值在枚举值(7.2)的范围内,则该值不变。否则,结果值将不确定(并且可能不在该范围内)。浮点类型的值也可以显式转换为枚举类型。结果值与将原始值转换为枚举的基础类型(4.9),然后转换为枚举类型相同。
7.2节说明了如何确定限制:
对于基础类型固定的枚举,该枚举的值是基础类型的值。否则,对于emin是最小的枚举数和emax是最大的枚举,该枚举的值是bmin到bmax范围内的值,定义如下:令K为2的补码表示形式,0为1的补码表示形式。补码或符号幅度表示。 bmax是大于或等于max(| emin |-K,| emax |)且等于2M -1的最小值,其中M是一个非负整数。如果emin为非负值,则bmin为零,否则为-(bmax + K)。如果bmin为零,则足以容纳枚举类型的所有值的最小位域的大小为max(M,1),否则为M +1。可以定义一个其枚举数未定义的枚举。如果枚举数列表为空,则枚举的值就像枚举有一个值为0的单个枚举数一样。
因此可能会在范围内将未定义的值强制转换为枚举,但是如果不在范围内,则它将为未定义。从理论上讲,可以定义一个具有较大值的枚举并确保强制转换可以正常工作,但是最好从枚举向整数类型进行反向转换并进行比较。