C++枚举类:强制转换为不存在的条目
C++ enum class: Cast to non existing entry
我在一个项目中遇到这种情况,我们有一些套接字通信,主要交换字符进行流控制。 我们将这些角色投射到开关中的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;
}
在此示例中,我有一个enum class : char
,其中包含一个未指定的条目和三个字符分配的条目。当我运行它时,我收到的输出是
ENUM2
DEFAULT
这似乎完美无缺,因为未定义的示例只是跳转到默认情况。但是,这是"保存要做"吗? 是否有一些我现在可能看不到的陷阱或其他并发症?
这是完全安全的,因为:
- 您的
enum class
是作用域枚举; - 您的枚举具有固定的基础类型
: char
; - 所以枚举的值是类型
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;
}
它并不完全安全。我发现C++标准[expr.static.cast]第10段陈述如下:
整型或枚举类型的值可以显式转换为枚举类型。如果原始值在枚举值 (7.2) 的范围内,则该值保持不变。否则,结果值未指定(并且可能不在该范围内)。浮点类型的值也可以显式转换为枚举类型。结果值与将原始值转换为枚举的基础类型 (4.9) 以及随后转换为枚举类型相同。
7.2 部分解释了如何确定限制:
对于基础类型为固定的枚举,枚举的值是基础类型的值。否则,对于 emin 是最小枚举器而 emax 是最大枚举器的枚举,枚举的值是 bmin 到 bmax 范围内的值,定义如下:设 K 为 1 表示二进制补码表示,0 表示 1 的补码或符号量级表示。bmax 是大于或等于 max(|emin| − K, |emax|) 且等于 2M − 1 的最小值,其中 M 是非负整数。如果 emin 为非负数,则 bmin 为零,否则为 −(bmax + K)。如果 bmin 为零,则大到足以容纳枚举类型的所有值的最小位字段的大小为 max(M, 1),否则为 M + 1。可以定义具有任何枚举器未定义的值的枚举。如果枚举器列表为空,则枚举的值就像枚举具有值为 0 的单个枚举器一样。
因此,如果未定义的值在该范围内,则可能会将未定义的值转换为枚举,但如果不是,则它是未定义的。从理论上讲,可以定义一个具有大值的枚举并确保强制转换然后工作,但最好从枚举向后转换到整数类型并进行比较。
- 我们可以访问一个不存在的联盟的成员吗
- C++:对不存在的命名空间使用命名空间指令
- 检查不带转换的扫描格式
- 显式 std::exception_ptr 转换为 bool 不存在.VS2010 错误?
- 不存在从"Magick::Color"到"MagickCore::Quantum"的合适转换功能
- C++不存在合适的转换函数
- 不存在从 "vector<string>" 到 "vector<string>" * 的合适转换?
- 不存在从"const std::string"到"time_t"的合适转换功能
- C++枚举类:强制转换为不存在的条目
- C++ 错误:不存在从 "math::Vec3<float>" 到 "float" 的合适转换函数
- 运算符重载:简单添加...错误 C2677:二进制"+":未找到采用类型 ___ 的全局运算符(或者不存在可接受的转换)
- 不存在从"std::string"到"char"的合适转换
- C++不存在从"Date"到"Date (*)()"的合适转换函数
- C++和Visual Studio错误 - 不存在从"std::basic_ostream<char, std::char_traits<char>>"到"int"的合适转换功
- 将相对路径转换为完整路径的系统功能,即使对于不存在的路径也适用
- 不存在从std字符串到const char*的合适转换函数
- 对于双向链表复制构造函数,不存在从 "const DListNode" 到 "DListNode *" 的合适转换函数
- Visual C++ 不存在从"myClass1"到"myClass1"的合适用户定义转换
- 智能感知:不存在从"std::string"到"char"的合适转换功能,那么我该怎么做?
- 映射和节点,错误智能感知:不存在从"const std::pair<const int, double>"到"Node"的合适用户定义转换