11

The title is already the question.
More details: the standard enacts:

If the enum-key is followed by a nested-name-specifier, the enum-specifier shall refer to an enumeration that was previously declared directly in the class or namespace to which the nested-name-specifier refers (i.e., neither inherited nor introduced by a using-declaration), and the enum-specifier shall appear in a namespace enclosing the previous declaration.

at 7.2, paragraph 4. For example, this prohibits to forward-declare an enum defined inside a class:

struct S{
  enum foo{A, B};
};

now, S can be forward-declared, while S::foo not.

Question is about why. Is there a situation in which this rule can be a benefit? Why is it needed? Or, if you prefer: if the standard had not this rule, is there a situation in which the compiler would have a problem? Which one(s)?

10
  • You can forward declare an enum class Commented Oct 5, 2016 at 9:52
  • yes in general, not if it is defined inside a class. Commented Oct 5, 2016 at 9:53
  • 1
    Can you forward declare classes inside classes? I've never seen that... Commented Oct 5, 2016 at 9:56
  • 1
    That would indicate a very questionable program design. Commented Oct 5, 2016 at 10:10
  • 1
    @skypjack, not everything that's interesting deserves to be implemented. Parts of a class cannot be forward declared outside of it, because they cannot be divorced. That's just sound design, and a reason to define a class to begin with. If a concept can be divorced from a class, it doesn't belong inside it to begin with. Commented Oct 5, 2016 at 10:28

3 Answers 3

6

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.

Sign up to request clarification or add additional context in comments.

2 Comments

So you say that the act of forward-declaring a scoped enum S::E; would be sort of telling the compiler: expect a class S, and an enum inside that class; that is, I would define partially a class. But then, the true class definition violates ODR. And with templates, one could forward-declare something and then write a specialization in contradiction to it. That's why it is forbidden. Did I catch your point?
More or less. The first is a brutal explanation of what could be behind, but a language-lawyer will probably gives a better and completely different explanation sooner or later. The example that uses templates serves the purpose of showing what could happen if it was allowed. In that case, the linker would return an error, but the language allows it, so what could one do to work around it? Well, forbid those kinds of definition is a good point from which to start indeed. :-)
3

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.

2 Comments

The question could quickly become - what's the reason for which you cannot declare anything outside a class (of course, apart for the fact that the standard says that)?
@skypjack Because, as I mentioned, a C++ class definition authoritatively declares the members of the class. This broad rule helps to implement the distinction between complete and incomplete classes.
-1

enum Enumeration; enum is not allowed to forward declare in header file.

enum class Enumeration; enum class is allowed to forward declare in header file.

1 Comment

enum Enumeration : Type is allowed to be forward declared

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.