为啥我不能使用双冒号在命名空间中前向声明一个类?
Posted
技术标签:
【中文标题】为啥我不能使用双冒号在命名空间中前向声明一个类?【英文标题】:Why can't I forward-declare a class in a namespace using double colons?为什么我不能使用双冒号在命名空间中前向声明一个类? 【发布时间】:2011-01-04 19:18:19 【问题描述】:class Namespace::Class;
为什么我必须这样做?:
namespace Namespace
class Class;
使用VC++ 8.0,编译器问题:
错误 C2653:“命名空间”:不是类或命名空间名称
我认为这里的问题是编译器无法判断Namespace
是类还是命名空间?但是为什么这很重要,因为它只是一个前向声明?
还有其他方法可以前向声明在某个命名空间中定义的类吗?上面的语法感觉就像我在“重新打开”命名空间并扩展它的定义。如果Class
实际上没有在Namespace
中定义怎么办?这会在某个时候导致错误吗?
【问题讨论】:
让我不同意这里的所有答案,并说这只是该语言的设计错误。他们可以想得更好。 这倾向于讨论为什么这在 C++ 中是非法的(这是主观的),并且看起来很有争议。投票结束。 编译器应该如何知道A::B
中的A
是命名空间标识符而不是类名?
@STingRaySC:讨论是主观的,因为没有明确的答案为什么 C++ 会这样做,所以我们在推测。 (这个问题是一个霰弹枪问题,有一些问题已经得到了客观的答案。)在这一点上,我对争论的痕迹变得敏感,而且你同意 Pavel 认为这是 C++ 的错误特征是合格的。如果Namespace
是一个类或命名空间,为什么它很重要,我不会有任何问题。只是不要暗示可能会引发一场针对语法的语言火焰战争。
【参考方案1】:
你得到了正确的答案,让我试着重新措辞:
class Namespace::Class;
为什么我必须这样做?
您必须这样做,因为术语 Namespace::Class
告诉编译器:
...好的,编译器。去寻找 命名空间命名空间,并在 引用名为 Class 的类。
但是编译器不知道你在说什么,因为它不知道任何名为Namespace
的命名空间。即使有一个名为Namespace
的命名空间,如:
namespace Namespace
;
class Namespace::Class;
它仍然行不通,因为您不能从该命名空间外部声明一个命名空间内的类。您必须在命名空间中。
因此,您实际上可以在命名空间中前向声明一个类。只需这样做:
namespace Namespace
class Class;
;
【讨论】:
所有其他答案都让我感到困惑,但是这个“你不能在命名空间之外从命名空间声明一个类。你必须在命名空间中。”是非常有用的提示。【参考方案2】:因为你不能。在 C++ 语言中,完全限定名称仅用于引用 现有(即先前声明的)实体。它们不能用于引入新实体。
而您正在实际上“重新打开”命名空间以声明新实体。如果类 Class
稍后被定义为不同命名空间的成员 - 它是一个完全不同的类,与您在此处声明的类无关。
一旦你到了定义预声明类的地步,你就不需要再次“重新打开”命名空间了。您可以在全局命名空间(或包含Namespace
的任何命名空间)中将其定义为
class Namespace::Class
/* whatever */
;
由于您指的是已在命名空间Namespace
中声明的实体,因此您可以使用限定名称Namespace::Class
。
【讨论】:
@STingRaySC:前向声明嵌套类的唯一方法是将声明放在封闭类的定义中。而且确实没有办法在定义封闭类之前前向声明嵌套类。 @STingRaySC:嵌套类可以被前向声明——看我的回答。 @John Dibling:嵌套类是在另一个类中声明的类。在命名空间内立即声明的类不是嵌套类。您的答案中没有任何关于 sensted 类的内容。【参考方案3】:我想这与你不能像这样一次性声明嵌套命名空间的原因相同:
namespace Company::Communications::Sockets
你必须这样做:
namespace Company
namespace Communications
namespace Sockets
【讨论】:
这实际上并不是解释为什么你不能这样做的答案。 这是一个为我节省了大量时间的答案 C++17 添加了这个。 这里你知道所有的都是命名空间。但是对于 Company::Communications::Socket 类,您不知道 Communications 是命名空间还是类(其中 socket 是嵌套类)。【参考方案4】:不清楚前向声明变量的实际类型是什么。前向声明 class Namespace::Class;
可能意味着
namespace Namespace
class Class;
或
class Namespace
public:
class Class;
;
【讨论】:
不是impossible to forward-declare a class that's embedded into a class吗? 我认为这是最好的答案之一,因为它回答了为什么编译器本身无法轻易确定。【参考方案5】:关于禁止它的理由有很多很好的答案。我只想提供无聊的标准条款,明确禁止它。这适用于 C++17 (n4659)。
有问题的段落是[class.name]/2:
仅由类键标识符组成的声明;要么是 在当前范围内重新声明名称或转发 将标识符声明为类名。它介绍了类 名称到当前范围内。
上面定义了前向声明(或类的红色声明)的构成。本质上,它必须是class identifier;
、struct identifier;
或union identifier;
之一,其中identifer 是[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
不是标识符。这是一个完全限定的名称,有些不同。
【讨论】:
以上是关于为啥我不能使用双冒号在命名空间中前向声明一个类?的主要内容,如果未能解决你的问题,请参考以下文章