强制转换为enum不能表示的枚举值是否合法?

Inn*_*der 9 c++ enums c++11

特定 enum class val { foo = 1, bar = 2, baz = 4 };

可以定义:

val operator|(val x, val y)
{
    return static_cast<val>(static_cast<int>(x) | static_cast<int>(y));
}
Run Code Online (Sandbox Code Playgroud)

但是,这样做在语义上是否正确?

我倾向于,如下所示,看似表现良好的例子:

int convert(val x)
{
    switch(x)
    {
    case val::foo: return 42;
    case val::bar: return 53;
    case val::baz: return 64;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用g ++和clang ++编译时,调用convert(val::foo | val::bar)将返回0.

是g ++版本.而这里是铛++版本.

我的问题是双重的:

  1. 将值存储在枚举器未表示的枚举中是否在语义上正确?我们非常欢迎该标准的摘录.

1.a在上面链接的例子中,哪个编译器是正确的,g ++或clang ++?

  1. 是否有标准(或建议)的方式来表示C++中的标志?

我可以想到几种可能的实现:

enum class val { foo, bar, baz, size };
using val_flags = std::set<val>; // (1)
using val_flags = std::vector<bool>; // (2)
using val_flags = std::bitset<val::size>; // (3)
using val_flags = std::underlying_type<val>::type; // (4)
Run Code Online (Sandbox Code Playgroud)

更新:

谢谢大家的答案.我最终复活了我的旧枚举运算符模板.如果有人感兴趣,可以在这里找到:github.com

Ben*_*igt 6

以下,看似表现良好的例子:

它不是,但做一个小的改变:

int convert(val x)
{
    switch(x)
    {
    case val::foo: return 42;
    case val::bar: return 53;
    case val::baz: return 64;
    }

    return 9; // ADDED THIS LINE
}
Run Code Online (Sandbox Code Playgroud)

一切都会好的.另一种解决方法是使用default:案例并返回那里.

您的现有代码通过到达具有非返回类型的函数的右括号来触发未定义的行为1void.因为它是未定义的行为,所以两个编译器都是正确的.

enum类型中保持值的语义是枚举值的按位或组合,是明确定义和保证的.该标准要求enumcan的实例存储任何整数值,而不使用比定义的任何枚举器值更多的位,其中包括所有按位OR组合.用来说这个的形式语言有点乱,但在这里(注意你的情况是一个enum class,这些总是有固定的底层类型,第一句适用):

对于其基础类型是固定的枚举,枚举的值是基础类型的值.否则,对于枚举,其中e min是最小的枚举数且e max是最大的,枚举的值是b min到b max范围内的值,定义如下:对于二进制补码表示,令K为1 0表示补码或符号幅度表示.b max是大于或等于max的最小值(| e min | - K,| e min |)并且等于2 M -1,其中M是非负整数.如果e min是非负的,则b min为零,否则为 - (b max + K).如果b min为零,则大小足以容纳枚举类型的所有值的最小位域的大小为max(M,1),否则为M + 1.可以定义具有未由其任何枚举​​器定义的值的枚举.如果枚举器列表为空,则枚举的值就像枚举具有值为0的单个枚举器一样.

(从n4582,第7.2节[dcl.enum])


1从6.6.3 [stmt.return]:

在构造函数,析构函数或具有cv void返回类型的函数的末尾流动等效于没有操作数的返回.否则,流出除main(3.6.1)以外的函数的末尾会导致未定义的行为.