devxlogo

C++0x Forward Enum Declarations Cut Down Compilation Time and Dependencies

C++0x Forward Enum Declarations Cut Down Compilation Time and Dependencies

very declaration of an enum type must be a complete definition containing a full list of its enumerators. However, in some use cases declaring an enumeration without providing the enumerators list is desirable. For example, you may use a forward declaration like this…

enum class Direction;

…instead of repeating the complete definition (see below) in every translation unit.

enum class Direction: short {Up, Down};

A year ago, the C++ standards committee voted into the Working Draft a proposal to add forward declarations of enum types. This 10-Minute Solution shows how to use forward declarations of enum types to expedite compilation time and reduce code dependencies.


Your project has an enum type containing hundreds of machine-generated enumerators that change frequently. You must provide a complete definition of that enum in every translation unit, even if the compiler seemingly doesn’t need the complete definition of the enum.


Replace complete definitions of enum types with forward enum declarations where possible.

The Repeated Enumerators List Problem

In many C++ projects, enum types that contain hundreds of enumerators are quite common. Often, these enum types are machine-generated and updated frequently. Examples of such huge enum types include a list of international airports, a list of locales supported by the application, and so on.

The problem is that whenever you update such an enum, every translation unit that references it must be compiled as well?even if the code that references the enum hasn’t changed. Consider the list of airports as an example:

//file: airports.h#ifndef AIRPORTS_H #define AIRPORTS_H enum class Airports: int{CPH,CDG,//..many more entriesLAX,LHR,//..many more entries};#endif

Seemingly, the following piece of code doesn’t depend on the list of Airports’ enumerators, as adding a new enumerator, changing an existing enumerator, or deleting one doesn’t affect Airports’ enum base (which is int):

#include "airports.h"std::istream& operator>>(std::istream& is, Airports& ap);std::string get_ap_name(Airports ap);Airports ap;std::cin >> ap;std::cout << get_ap_name(ap);

Theoretically, the compiler can process this code without needing a complete definition of Airports, but that isn't the case. C++03 requires the presence of the entire list of enumerators to determine the underlying type of Airports. This leads to a longer compilation time and increases the risk of ODR(One Definition Rule) violations.

Scoped Enums and Enum Bases

Before diving deeper into forward declaration of enum types, you need to understand two key concepts: scoped enum types and enum bases. A scoped enum is one that is declared with the class keyword, as opposed to a traditional enum, which is unscoped.

Among other things, scoped and unscoped enums differ with respect to enumerator qualification:

enum class A {x,y}; //scopedenum B {t,u}; //unscopedA a1=A::x; //OK, qualified enumeratorA a2=y; //error, y is unqualifiedB b=t; //OK, unscoped. Enumerator isn't qualified

As previously stated, an enum base is the underlying integral type of the enumeration. Recall that the compiler represents every enum type as an unspecified integral type that is large enough to hold every enumerator value. For scoped enums, the default base type is int. You can override it by specifying a different enum base explicitly, like this:

enum class A {x,y}; //scoped, base is intenum class Q: char {x,y}; //scoped, base is char

In C++0x, an unscoped enum may also include an explicit enum base. Notice, however, that unlike scoped enums, an unscoped enum doesn't have a default enum base:

enum C: int {x,y}; //unscoped, enum base is intenum D {x,y}; //unscoped, no enum base

This presents a problem: the compiler must know what the enum base is to compile any piece of code that refers to that enum. Now you are ready to learn how forward declarations solve this problem.

Forward Declarations of Enum Types

C++0x introduces a new syntactic construct?the forward declaration of an enum type?that allows you to declare an enumeration without providing its enumerators list. Such a declaration doesn't constitute a definition (in order to avoid problems with ODR). You can provide a forward declaration of an enum type only for enumerations with a fixed underlying type. You can redeclare an enumeration later (possibly providing the missing list of enumerators), but such a redeclaration must match the previous declaration. The following examples demonstrate these rules:

//C++0xenum E: char;  // OK. forward declaration of an unscoped                  //enum with char as the enum baseenum F; // Error. Enum base is required in fwd decl.enum class G: short; //OK. Scoped, enum base is shortenum class H; //OK. Scoped, default enum base is intenum E: char; //OK. Redeclaration of Eenum class E: char; //Error. Previously declared unscopedenum E: int; //Error. Different enum baseenum class E: char {i,j,k,l}; // OK: this redeclaration is a definitionenum class E: char; //OK. Fwd decl.

Returning to the Airports example, you can now use the following forward declaration of Airports to compile the same code:

//C++0xenum class Airports;std::istream& operator>>(std::istream& is, Airports& ap);std::string get_ap_name(Airports ap);Airports ap;  std::cin >> ap;std::cout << get_ap_name(ap);

Because Airports is a scoped enum, its default enum base is int. When the compiler sees the following forward declaration, it knows what the underlying type of Airports is without looking at a list of enumerators:

enum class Airports;

In the above code listing, changing the list of Airports' enumerators doesn't require you to include the complete definition of Airports, so long as the enum base is the same. In this regard, enum forward declarations are similar to forward declarations of classes, structs, and unions.

Currently, a few compilers already support forward declarations of enum types, including C++ Builder 2009. Give this new C++0x feature a try and you may say goodbye to the long compilation times and ODR violations that C++03's enumerator list requirements can cause.

devx-admin

Share the Post: