Keywords: C++ | enum | forward declaration | information hiding | C++11
Abstract: This article provides an in-depth exploration of forward declaration for enumeration types in C++, analyzing the fundamental reasons why enums could not be forward-declared in traditional C++03—primarily due to the compiler's need to determine storage size. It details how C++11's enum classes and enums with specified underlying types resolve this issue, with practical code examples demonstrating correct usage in modern C++. The discussion also covers best practices for information hiding and interface design, offering comprehensive guidance for C++ developers.
The Historical Challenge of Enum Forward Declaration
In traditional C++ programming, developers frequently encountered the limitation that enumeration types could not be forward declared. Consider the following code example:
enum E;
void Foo(E e);
enum E {A, B, C};
This code results in a compilation error under the C++03 standard, fundamentally because the compiler needs to determine the storage size of the enum variable.
Determining Storage Size
According to Section 7.2.5 of the ISO C++ Standard, the underlying type of an enumeration is an integral type capable of representing all enumerator values. The compiler implementation may choose the specific storage type based on the size required by the enum values—it could be a char, an int, or another integral type.
The standard explicitly states: "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."
Considerations for Function Call Stack
When a function call occurs, the caller must know the exact size of parameters to correctly set up the call stack. If only a forward declaration of the enum is available without the complete definition, the translation unit cannot determine what storage size the compiler has chosen for that enum. This uncertainty made forward declaration infeasible in traditional C++.
C++11 Solutions
The C++11 standard introduced two methods to resolve the enum forward declaration issue:
Enums with Underlying Type
By explicitly specifying the underlying type of the enum, the compiler can determine the storage size at the declaration point:
enum E : unsigned int; // Legal C++11 forward declaration
void Foo(E e);
enum E : unsigned int {
VALUE_1,
VALUE_2,
VALUE_3
};
Enum Classes
Enum classes default to using int as the underlying type, allowing direct forward declaration:
enum class E; // Legal C++11 forward declaration
void Foo(E e);
enum class E {
VALUE_1,
VALUE_2,
VALUE_3
};
Practical Application Scenarios
Enum forward declaration is particularly useful for information hiding and interface design. Consider a scenario where a class needs private methods that use a specific enum but should not expose the enum values:
// header.h
class MyClass {
private:
enum class InternalEnum;
void privateMethod(InternalEnum e);
public:
void publicMethod();
};
// implementation.cpp
enum class MyClass::InternalEnum {
FUNCTIONALITY_NORMAL,
FUNCTIONALITY_RESTRICTED,
FUNCTIONALITY_FOR_PROJECT_X
};
void MyClass::privateMethod(InternalEnum e) {
// Implementation details
}
This design allows distributing the header file to users while hiding specific implementation details and project-related information.
Compiler Support
Modern C++ compilers provide good support for enum forward declaration:
- GCC 4.6 and above (using
-std=c++11or-std=c++14flags) - Visual C++ 2013 and above
- Clang 3.0 and above
Important Considerations
When using enum forward declaration, keep in mind:
- The underlying type must be consistent between forward declaration and complete declaration
- If a function does not need to access specific enum values, forward declaration is sufficient
- Code that requires access to enum values must include the complete enum definition
By properly utilizing the enum forward declaration features introduced in C++11, developers can create more modular and information-hiding code structures while maintaining the advantages of type safety and compile-time checks.