Why cannot forward declare a scoped enum?

A scoped enum is declared with enum class (or enum struct, not with struct { enum …. That would be an unscoped enumeration, in the scope of a class.

struct S {
    enum foo {A, B}; // Not a scoped enumeration.
};

A scoped enumeration can be forward-declared inside a class and defined outside:

struct S {
    enum class foo;
};

enum class S::foo { A, B };

However, you cannot declare a class member outside the class, unless it was already declared and you're defining it. Allowing member declarations outside would go against the principle that a class { } definition declares all the class members, that C++ classes are "closed."

Put another way, the rules for declaring and defining member scoped enumerations are essentially the same as for member functions or member classes.


At least, if forward-declare an enum was allowed, it would have created problems with template specializations like the one in the following example:

// somewhere in a .cpp

template<typename>
struct S;

enum S<int>::E;

// somewhere in a galaxy far, far away

template<typename>
struct S { enum class E {}; };

template<>
struct S<int> {};

How could the compiler know (and verify) that enum S<int>::E; is actually defined?


That said, even when you deal with namespaces you cannot do this:

struct X::A;
namespace X { struct A {}; }

But you can do this:

namespace X { struct A; }
namespace X { struct A {}; }

Using classes would result in a code like the following one:

struct A { enum E; };
struct A { enum E {} };

Anyway, this would violate the odr and it is not allowed.


Now, I'll try to give you my impression about the why.
If a forward-declaration of that type was allowed, you would have been allowed to give a partial definition of the containing class.
In other terms, consider this: enum S::E. This states firmly that S contains the enum class E, thus you are giving a clue about the definition of S. To speak not in standardese (that is far from being my natural language) you are partially defining S, thus the compiler should know that S has its definition somewhere plus it must have a definition for E too (either as part of the primary definition or as an out-of-class definition).
This would break the odr rules when the actual definition comes into view, so it cannot be allowed in any case, but as an exception of the basics rules of the language.
Moreover, this is a great source of headaches.

My two cents.

Tags:

C++

C++11