Is a class declaration allowed after a class definition?
Asked Answered
G

2

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?

Greff answered 22/9, 2015 at 11:25 Comment(9)
It simply becomes a declaration.Armet
I don't understand what you mean: The forward declaration becomes a declaration?Greff
Given that it is illegal inside a class, is my example code Says who?Left
@SimonKraemer, Jonathan Wakely, in the linked answer.Greff
BTW: Forward declarations after the definition happens all the time e.g. dependend on the order you include the headers in a cpp.Left
I mean it looses the adverb forward and becomes a simple declaration.Armet
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.Obturate
@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.Greff
@101010 I removed 'forward' where not applicable, thanks.Greff
G
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.

Grose answered 22/9, 2015 at 11:54 Comment(2)
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?Greff
It is explicit in quoted text, but not in your examples: forward declarations are legal even within a class definition.Cathicathie
A
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).

Armet answered 22/9, 2015 at 11:44 Comment(2)
And inside namespaces?Greff
@Greff and inside namespace are also legal.Armet

© 2022 - 2024 — McMap. All rights reserved.