Why can't I forward-declare a class in a namespace using double colons?

class Namespace::Class;

Why do I have to do this?:

namespace Namespace {
class Class;
}

Using VC++ 8.0, the compiler issues:

error C2653: 'Namespace' : is not a class or namespace name

I assume that the problem here is that the compiler cannot tell whether Namespace is a class or a namespace? But why does this matter since it's just a forward declaration?

Is there another way to forward-declare a class defined in some namespace? The syntax above feels like I'm "reopening" the namespace and extending its definition. What if Class were not actually defined in Namespace? Would this result in an error at some point?

84906 次浏览

因为你不能。在 C + + 语言中,完全限定名仅用于引用 存在(即先前声明的)实体。它们不能用于引入 新的实体。

而且 实际上“重新打开”名称空间来声明新的实体。如果类 Class后来被定义为不同名称空间的成员-它是一个完全不同的类,与您在这里声明的类没有任何关系。

一旦到达预先声明的类 定义的位置,就不需要再次“重新打开”名称空间。可以在全局命名空间(或包含 Namespace的任何命名空间)中将其定义为

class Namespace::Class {
/* whatever */
};

由于引用的是已经在名称空间 Namespace中声明的实体,因此可以使用限定名 Namespace::Class

你得到了正确的答案,让我重新措辞一下:

class Namespace::Class;

我为什么要这么做?

你必须这样做,因为术语 Namespace::Class告诉编译器:

... 好吧,编译器。去找到 命名空间,并且在 引用名为 Class 的类的。

但是编译器不知道您在说什么,因为它不知道任何名称空间 Namespace。即使有一个名为 Namespace的名称空间,如:

namespace Namespace
{
};


class Namespace::Class;

它仍然无法工作,因为您不能在一个名称空间内从该名称空间之外声明一个类。必须在名称空间中。

因此,您实际上可以在一个名称空间中转发声明一个类:

namespace Namespace
{
class Class;
};

我想,这与您不能像下面这样一次性声明嵌套名称空间的原因是一样的:

namespace Company::Communications::Sockets {
}

你必须这么做:

namespace Company {
namespace Communications {
namespace Sockets {
}
}
}

关于不允许这种行为的理由,有很多很好的答案。我只是想提供一些无聊的标准条款明确禁止这样做。对于 C + + 17(n4659)也是如此。

该段是 [ class.name ]/2:

仅由 类密钥标识符组成的声明; 是 在当前范围或转发中重新声明名称 将标识符声明为类名 名称放入当前范围。

上面定义了什么构成了一个前向声明(或者一个类的重新声明)。从本质上讲,它必须是 class identifier;struct identifier;union identifier;中的一个,其中 身份证明[ lex.name ]中常见的词汇定义:

identifier:
identifier-nondigit
identifier identifier-nondigit
identifier digit
identifier-nondigit:
nondigit
universal-character-name
nondigit: one of
a b c d e f g h i j k l m
n o p q r s t u v w x y z
A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z _
digit: one of
0 1 2 3 4 5 6 7 8 9

这就是我们所熟悉的 [a-zA-Z_][a-zA-Z0-9_]*常用方案的生产。正如你所看到的,这使得 class foo::bar;不能成为一个有效的前向声明,因为 foo::bar不是一个标识符。这是个完全限定的名字,有点不一样。

前向声明的变量的类型实际上是什么并不清楚,前向声明 class Namespace::Class;可能意味着什么

namespace Namespace {
class Class;
}

或者

class Namespace {
public:
class Class;
};