是否允许枚举具有未列出的值?

时间:2015-11-19 19:44:20

标签: c++ c enums language-lawyer

说,我们有

enum E
{
  Foo = 0,
  Bar = 1
};

现在,我们

enum E v = ( enum E ) 2;

然后

switch ( v )
{
  case Foo:
    doFoo();
  break;
  case Bar:
    doBar();
  break;
  default:
    // Is the compiler required to honor this?
    doOther();
  break;
}

由于上面的开关处理枚举的每个可能列出的值,是否允许编译器优化掉上面的default分支,或者在枚举值为en的情况下具有未指定或未定义的行为。不在列表中?

由于我期望C和C ++的行为应该类似,所以问题在于两种语言。但是,如果在这种情况下C和C ++之间存在差异,那么了解它也会很好。

6 个答案:

答案 0 :(得分:22)

C ++情况

在C ++中,每个枚举都有一个底层整数类型。它可以修复,如果明确指定(例如:enum test2 : long { a,b};),或者如果范围,则默认为int枚举(例如:enum class test { a,b };):

  

[dcl.enum] / 5:每个枚举都定义了一个与所有其他类型不同的类型。每个枚举还有一个底层类型。 (...) 如果不   显式指定,作用域枚举类型的基础类型   是int。在这些情况下,基础类型被认为是固定的。

对于 unscoped 枚举,其中基础类型未明确修复(您的示例),标准为您的编译器提供了更大的灵活性:

  

[dcl.enum] / 7:对于其基础类型未修复的枚举,基础类型是一个可以代表所有的整数类型   枚举中定义的枚举器值。 (...)它是实现定义的,使用整数类型作为底层   除了底层类型不应大于int之外的类型   除非枚举器的值不能适合int或unsigned   中间体

现在是非常棘手的事情:枚举变量可以保存的值取决于基础类型是否已修复

  • 如果它已修复,"枚举的值是 基础类型。"

  • 除此之外,它是可以容纳最小枚举数和最大枚举数的最小位域的最小值和最大值内的积分值。

您处于第二种情况,虽然您的代码适用于大多数编译器,但最小的位域大小为1,因此您可以确保在所有兼容的C ++编译器上保留的唯一值是介于0和1之间的值。 ..

结论:如果要确保该值可以设置为2,则必须使枚举为枚举枚举,或明确指示基础类型。**

更多阅读:

C情况

C情况要简单得多(C11):

  

6.2.5 / 16:枚举包含一组命名的整数常量值。每个不同的枚举构成不同的枚举   类型。

所以基本上,它是一个int:

  

6.7.2.2./2 定义枚举常量值的表达式应为具有值的整数常量表达式   可表示为int。

具有以下限制:

  

每个枚举类型应与char(一个有符号整数)兼容   类型或无符号整数类型。类型的选择是   实现定义,但应能够代表   枚举的所有成员的值。

答案 1 :(得分:3)

在C中,enum类型是一个足以容纳所有enum常量的整数类型:

  

(C11,6.7.2.2p4)“每个枚举类型应与char,有符号整数类型或无符号整数类型兼容。类型的选择是实现定义的,110)但应能够表示枚举的所有成员的值“。

假设enum E的所选类型为_Bool_Bool对象只能存储值01。在不调用未定义行为的情况下,不能让_Bool对象存储不同于01的值。

在这种情况下,允许编译器假定enum E类型的对象只能在严格符合的程序中保留01,并且允许编译器优化default切换案例。

答案 2 :(得分:0)

C ++ Std 7.2.7 [dcl.enum]:

  

可以定义一个枚举,该枚举的值不是由任何一个枚举器定义的。

因此,您可以使用枚举值列表中未列出的枚举值。

但在您的具体情况下,“基础类型”不是“固定的”(7.2.5)。规范没有说明在这种情况下哪种是基础类型,但它必须是整数。由于char是最小的类型,我们可以得出结论,枚举器列表中没有指定枚举的其他值。

顺便说一下,我认为当编译器可以确定没有其他值分配给v时,编译器可以优化你的情况,这是安全的,但我认为没有那些聪明的编译器。

答案 3 :(得分:0)

另外,7.2 / 10:

  

算术或枚举类型的表达式可以转换为   枚举类型显式。如果它在,则值不变   枚举类型的枚举值范围;否则   结果枚举值未指定。

答案 4 :(得分:-1)

在C枚举器中,类型为int。因此,可以将任何整数值分配给枚举类型的对象。

来自C标准(6.7.2.2枚举说明符)

  

3枚举器列表中的标识符声明为常量   类型 int ,可能出现在允许的地方。

在C ++中,枚举器具有定义它的枚举类型。在C ++中,您应该expliicitly指定底层类型,或者编译器自己计算最大允许值。

来自C ++标准(7.2枚举声明)

5每个枚举定义一个与所有其他类型不同的类型。每个枚举也有一个基础类型。可以使用enum-base明确指定基础类型;如果未明确指定,则作用域枚举类型的基础类型为int。在这些情况下,据说基础类型是固定的。在enum-specifier的右括号之后,每个枚举器都有其枚举类型

因此在C中,枚举的任何可能值都是任何整数值。编译器可能无法优化删除默认标签的开关。

答案 5 :(得分:-1)

在C和C ++中,这可以工作。

两者的代码相同:

#include <stdio.h>

enum E
{
  Foo = 0,
  Bar = 1
};

int main()
{
    enum E v = (enum E)2;    // the cast is required for C++, but not for C
    printf("v = %d\n", v);
    switch (v) {
    case Foo:
        printf("got foo\n");
        break;
    case Bar:
        printf("got bar\n");
        break;
    default:
        printf("got \n", v);
        break;
    }
}

两者的输出相同:

v = 2
got default

在C中,enum是一个整数类型,因此您可以为其指定一个整数值而不进行强制转换。在C ++中,enum是它自己的类型。