5

I just read this answer, and it completely puzzles me.

I was always thinking a class declaration can appear many times, and only the definition has to exist only once, like:

/*class Class {*/

    class A;         // (1) forward declaration

    class A {        // (2) definition, only once
       int m;
    };

    class A;         // (3) declaration again, legal?

    class A a;       // (4) declaration again, legal?

/*};*/

From the linked answer: (3) (and (4)?) is illegal if the code above is nested inside a class (definition and declarations of class A are nested inside class Class).

On cppreference, I found an example of the above, not nested:

struct s { int a; };
struct s; // does nothing (s already defined in this scope)
void g() {
    struct s; // forward declaration of a new, local struct "s"
              // this hides global struct s until the end of this block
    s* p;     // pointer to local struct s
    struct s { char* p; }; // definitions of the local struct s
}

See the second line.

Question: Given that it is illegal inside a class, is my example code, and the cppreference example above, legal when not nested inside a class? Or more generally: When can a class declaration follow a definition (how is it inside namespaces for example)? If it is legal, why is there a difference?

Community
  • 1
  • 1
alain
  • 11,939
  • 2
  • 31
  • 51
  • 1
    It simply becomes a declaration. – 101010 Sep 22 '15 at 11:27
  • I don't understand what you mean: The *forward* declaration becomes a declaration? – alain Sep 22 '15 at 11:31
  • _Given that it is illegal inside a class, is my example code_ Says who? – Simon Kraemer Sep 22 '15 at 11:31
  • @SimonKraemer, Jonathan Wakely, in the linked answer. – alain Sep 22 '15 at 11:32
  • BTW: Forward declarations after the definition happens all the time e.g. dependend on the order you include the headers in a cpp. – Simon Kraemer Sep 22 '15 at 11:33
  • I mean it looses the adverb `forward` and becomes a simple declaration. – 101010 Sep 22 '15 at 11:36
  • 4
    Rules for classes are C++ only rules. A class is declared in one piece, and has no need for multiple forward declarations of the same item. Declarations outside classes mostly follow the less strict C rules, for compatibility. – Bo Persson Sep 22 '15 at 11:37
  • @Barry, the part about namespaces, and the why. Bo Perssons comment answers the why a bit, but I still wonder why the rules have been made more strict, because it makes preprocessor-'generated' headers more error-prone, like in the linked question. – alain Sep 22 '15 at 11:46
  • @101010 I removed 'forward' where not applicable, thanks. – alain Sep 22 '15 at 13:01

2 Answers2

8

From [basic.def]:

A declaration (Clause 7) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations.

From [class.name]:

A declaration consisting solely of class-key identifier; is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name. It introduces the class name into the current scope.

So it's generally legal to do this. There's just the one exception in [class.mem]:

A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined, and except that an enumeration can be introduced with an opaque-enum-declaration and later redeclared with an enum-specifier.

Perfectly OK in namespace scope, not allowed in class scope.


As to why? Well, this rule let's you "forward" declare all the classes you need everywhere you would typically be allowed to do so:

// a.h
struct C;

struct A {
    C* c;
};

// b.h
struct C;

struct B {
    C& c;
};

without having to worry about somebody actually including the full declaration and breaking everything for you:

// d.h
#include "c.h"
#include "a.h" // now C was already declared!
#include "b.h" // and here too!

struct D { ... };

This isn't so much of a concern within a class definition. It can't exactly span multiple files. So the inability to redeclare nested types doesn't actually achieve anything.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thanks, I think this pretty much nails it. And because there is no real advantage in allowing it inside classes, it's better to catch programmer errors by disallowing it? – alain Sep 22 '15 at 12:07
  • It is explicit in quoted text, but not in your examples: **forward** declarations are legal even within a class definition. – Serge Ballesta Sep 22 '15 at 12:22
2

class A; This is a forward declaration of incomplete class A (legal).

class A { int m; }; This is the definition of Class A (legal).

class A; This is re-declaration of class A (legal).

class A a; This is declaration of object a of type A (legal).

101010
  • 41,839
  • 11
  • 94
  • 168