class 定义后是否允许使用 class 声明?

Is a class declaration allowed after a class definition?

刚看完this answer,完全不解。

我一直在想一个class声明可以出现很多次,而定义只需要存在一次,比如:

/*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?

/*};*/

来自链接的答案:如果上面的代码 嵌套在 class 中,则 (3)(和 (4)?)是非法的class A 的定义和声明嵌套在 class Class 中)。

cppreference上找到了上面的例子,没有嵌套:

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
}

见第二行。

问题:鉴于在class内部是非法的,是我的示例代码,以及上面的cppreference示例,当不是 嵌套在 class 中?或者更一般地说:class 声明什么时候可以跟在定义之后(例如,它在名称空间中是怎样的)?如果合法,为什么会有差异?

class A; 这是不完整的前向声明class A(合法)。

class A { int m; };这是Class A的定义(合法)。

class A; 这是 class A 的重新声明(合法)。

class A a; 这是类型 A(合法)的对象 a 的声明。

来自[basic.def]:

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

来自[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.

所以这样做通常是合法的。 [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.

在命名空间范围内完全可以,在 class 范围内不允许。


至于为什么?好吧,这条规则让你 "forward" 在你通常被允许这样做的任何地方声明你需要的所有 classes:

// a.h
struct C;

struct A {
    C* c;
};

// b.h
struct C;

struct B {
    C& c;
};

无需担心有人实际包含完整声明并为您破坏一切:

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

struct D { ... };

在 class 定义 中,这并不是什么大问题。它不能完全跨越多个文件。因此,无法重新声明嵌套类型实际上并没有实现任何目标。