Forward declaring an enum in C++

2019-01-02 14:41发布

I'm trying to do something like the following:

enum E;

void Foo(E e);

enum E {A, B, C};

which the compiler rejects. I've had a quick look on Google and the consensus seems to be "you can't do it", but I can't understand why. Can anyone explain?

Clarification 2: I'm doing this as I have private methods in a class that take said enum, and I do not want the enum's values exposed - so, for example, I do not want anyone to know that E is defined as

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

as project X is not something I want my users to know about.

So, I wanted to forward declare the enum so I could put the private methods in the header file, declare the enum internally in the cpp, and distribute the built library file and header to people.

As for the compiler - it's GCC.

标签: c++ enums
18条回答
只靠听说
2楼-- · 2019-01-02 14:48

I'd do it this way:

[in the public header]

typedef unsigned long E;

void Foo(E e);

[in the internal header]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };

By adding FORCE_32BIT we ensure that Econtent compiles to a long, so it's interchangeable with E.

查看更多
墨雨无痕
3楼-- · 2019-01-02 14:49

Forward declaring things in C++ is very useful because it dramatically speeds up compilation time. You can forward declare several things in C++ including: struct, class, function, etc...

But can you forward declare an enum in C++?

No you can't.

But why not allow it? If it were allowed you could define your enum type in your header file, and your enum values in your source file. Sounds like it should be allowed right?

Wrong.

In C++ there is no default type for enum like there is in C# (int). In C++ your enum type will be determined by the compiler to be any type that will fit the range of values you have for your enum.

What does that mean?

It means that your enum's underlying type cannot be fully determined until you have all of the values of the enum defined. Which mans you cannot separate the declaration and definition of your enum. And therefore you cannot forward declare an enum in C++.

The ISO C++ standard S7.2.5:

The underlying type of an enumeration 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 for an enumeration 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. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0. The value of sizeof() applied to an enumeration type, an object of enumeration type, or an enumerator, is the value of sizeof() applied to the underlying type.

You can determine the size of an enumerated type in C++ by using the sizeof operator. The size of the enumerated type is the size of its underlying type. In this way you can guess which type your compiler is using for your enum.

What if you specify the type of your enum explicitly like this:

enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);

Can you then forward declare your enum?

No. But why not?

Specifying the type of an enum is not actually part of the current C++ standard. It is a VC++ extension. It will be part of C++0x though.

Source

查看更多
闭嘴吧你
4楼-- · 2019-01-02 14:50

Because the enum can be an integral size of varying size (the compiler decides which size a given enum has), the pointer to the enum can also have varying size, since it's an integral type (chars have pointers of a different size on some platforms for instance).

So the compiler can't even let you forward-declare the enum and user a pointer to it, because even there, it needs the size of the enum.

查看更多
情到深处是孤独
5楼-- · 2019-01-02 14:50

You define an enumeration to restrict the possible values of elements of the type to a limited set. This restriction is to be enforced at compile time.

When forward declaring the fact that you will use a 'limited set' later on doesn't add any value: subsequent code needs to know the possible values in order to benefit from it.

Although the compiler is concerned about the size of the enumerated type, the intent of the enumeration gets lost when you forward declare it.

查看更多
旧人旧事旧时光
6楼-- · 2019-01-02 14:51

There's some dissent since this got bumped (sort of), so here's some relevant bits from the standard. Research shows that the standard doesn't really define forward declaration, nor does it explicitly state that enums can or can't be forward declared.

First, from dcl.enum, section 7.2:

The underlying type of an enumeration 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 for an enumeration 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. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0. The value of sizeof() applied to an enumeration type, an object of enumeration type, or an enumerator, is the value of sizeof() applied to the underlying type.

So the underlying type of an enum is implementation-defined, with one minor restriction.

Next we flip to the section on "incomplete types" (3.9), which is about as close as we come to any standard on forward declarations:

A class that has been declared but not defined, or an array of unknown size or of incomplete element type, is an incompletely-defined object type.

A class type (such as "class X") might be incomplete at one point in a translation unit and complete later on; the type "class X" is the same type at both points. The declared type of an array object might be an array of incomplete class type and therefore incomplete; if the class type is completed later on in the translation unit, the array type becomes complete; the array type at those two points is the same type. The declared type of an array object might be an array of unknown size and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points ("array of unknown bound of T" and "array of N T") are different types. The type of a pointer to array of unknown size, or of a type defined by a typedef declaration to be an array of unknown size, cannot be completed.

So there, the standard pretty much laid out the types that can be forward declared. Enum wasn't there, so compiler authors generally regard forward declaring as disallowed by the standard due to the variable size of its underlying type.

It makes sense, too. Enums are usually referenced in by-value situations, and the compiler would indeed need to know the storage size in those situations. Since the storage size is implementation defined, many compilers may just choose to use 32 bit values for the underlying type of every enum, at which point it becomes possible to forward declare them. An interesting experiment might be to try forward declaring an enum in visual studio, then forcing it to use an underlying type greater than sizeof(int) as explained above to see what happens.

查看更多
零度萤火
7楼-- · 2019-01-02 14:52

[My answer is wrong, but I've left it here because the comments are useful].

Forward declaring enums is non-standard, because pointers to different enum types are not guaranteed to be the same size. The compiler may need to see the definition to know what size pointers can be used with this type.

In practice, at least on all the popular compilers, pointers to enums are a consistent size. Forward declaration of enums is provided as a language extension by Visual C++, for example.

查看更多
登录 后发表回答