Is it allowed for an enum to have an unlisted value? [duplicate]

混江龙づ霸主 提交于 2019-11-30 05:39:11

C++ situation

In C++, each enum has an underlying integral type. It can be fixed, if it is explicitly specified (ex: enum test2 : long { a,b};) or if it is int by default in the case of a scoped enum (ex: enum class test { a,b };):

[dcl.enum]/5: Each enumeration defines a type that is different from all other types. Each enumeration also has an underlying type. (...) if not explicitly specified, the underlying type of a scoped enumeration type is int. In these cases, the underlying type is said to be fixed.

In the case of an unscoped enum where the underlying type was not explicitely fixed (your example), the standard gives more flexibility to your compiler:

[dcl.enum]/7: For an enumeration whose underlying type is not fixed, the underlying type is an integral type that can represent all the enumerator values defined in the enumeration. (...) It is implementation-defined which integral type is used as the underlying type except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int.

Now a very tricky thing: the values that can be held by an enum variable depends on whether or not the underlying type is fixed:

  • if it's fixed, "the values of the enumeration are the values of the underlying type."

  • otherwhise, it is the integral values within the minimum and the maximum of the smallest bit-field that can hold the smallest enumerator and the largest one.

You are in the second case, although your code will work on most compilers, the smalest bitfield has a size of 1 and so the only values that you can for sure hold on all compliant C++ compilers are those between 0 and 1...

Conclusion: If you want to ensure that the value can be set to 2, you either have to make your enum a scoped enum, or explicitly indicate an underlying type.**

More reading:

C situation

The C situation is much simpler (C11):

6.2.5/16: An enumeration comprises a set of named integer constant values. Each distinct enumeration constitutes a different enumerated type.

So basically, it is an int:

6.7.2.2./2 The expression that defines the value of an enumeration constant shall be an integer constant expression that has a value representable as an int.

With the following restriction:

Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration.

In C an enum type is an integer type large enough to hold all the enum constants:

(C11, 6.7.2.2p4) "Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined,110) but shall be capable of representing the values of all the members of the enumeration".

Let's say the selected type for enum E is _Bool. A _Bool object can only store the values 0 and 1. It's not possible to have a _Bool object storing a value different than 0 or 1 without invoking undefined behavior.

In that case the compiler is allowed to assume that an object of the enum E type can only hold 0 or 1 in a strictly conforming program and is so allowed to optimize out the default switch case.

Kevin C

C++Std 7.2.7 [dcl.enum]:

It is possible to define an enumeration that has values not defined by any of its enumerators.

So, you can have enumeration values which are not listed in enumerator list.

But in your specific case, the 'underlying type' is not 'fixed' (7.2.5). The specification doesn't say which is the underlying type in that case, but it must be integral. Since char is the smallest such type, we can conclude that there are other values of the enum which are not specified in the enumerator list.

Btw, I think that the compiler can optimize your case when it can determine that there are no other values ever assigned to v, which is safe, but I think there are no compilers which are that smart yet.

Also, 7.2/10:

An expression of arithmetic or enumeration type can be converted to an enumeration type explicitly. The value is unchanged if it is in the range of enumeration values of the enumeration type; otherwise the resulting enumeration value is unspecified.

In C enumerators have type int . Thus any integer value can be assigned to an object of the enumeration type.

From the C Standard (6.7.2.2 Enumeration specifiers)

3 The identifiers in an enumerator list are declared as constants that have type int and may appear wherever such are permitted.

In C++ enumerators have type of the enumeration that defines it. In C++ you should either expliicitly to specify the underlaying type or the compiler calculates itself the maximum allowed value.

From the C++ Standard (7.2 Enumeration declarations)

5 Each enumeration defines a type that is different from all other types. Each enumeration also has an underlying type. The underlying type can be explicitly specified using enum-base; if not explicitly specified, the underlying type of a scoped enumeration type is int. In these cases, the underlying type is said to be fixed. Following the closing brace of an enum-specifier, each enumerator has the type of its enumeration.

Thus in C any possible value of a enum is any integer value. The compiler may not optimize a switch removing the default label.

In C and C++, this can work.

Same code for both:

#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;
    }
}

Same output for both:

v = 2
got default

In C, an enum is an integral type, so you can assign an integer value to it without casting. In C++, an enum is its own type.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!