C++ 枚举类:转换为不存在的条目

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)

这似乎完美无缺,因为未定义的示例只是跳转到默认情况。然而,这是“保存要做”吗?是否有一些我现在可能看不到的陷阱或其他并发症?

Chr*_*phe 7

这是完全安全的,因为:

  • yourenum class是一个作用域枚举;
  • 您的枚举具有固定的基础类型: char
  • 所以枚举的值是 type 的值char
  • 因此将 char 值转换为枚举是完全有效的。

这里的 C++17 标准引用与上述语句相对应:

[dcl.enum]/2: (...) 枚举键enum classenum 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)