为什么不能使用双冒号在名称空间中向前声明一个类?


164
class Namespace::Class;

为什么我必须这样做?:

namespace Namespace {
    class Class;
}

使用VC ++ 8.0时,编译器会出现以下问题:

错误C2653:“名称空间”:不是类或名称空间名称

我认为这里的问题是编译器无法判断Namespace类还是名称空间?但是为什么这很重要,因为它只是一个前向声明?

有没有其他方法可以向前声明在某些名称空间中定义的类?上面的语法就像我在“重新打开”命名空间并扩展其定义一样。如果Class未在中定义Namespace怎么办?在某个时候会导致错误吗?


44
让我不同意这里的所有答案,并说这仅仅是该语言的设计错误。他们可以考虑得更好。
帕维尔·拉兹维洛夫斯基

这倾向于讨论为什么这在C ++中是非法的(这是主观的),并且看起来很有争议。投票关闭。
David Thornley,2010年

7
编译器应该如何知道中A::BA是名称空间标识符而不是类名?
David R Tribble 2010年

@STingRaySC:讨论是主观的,因为没有一个清楚的答案为什么C ++会这样做,所以我们推测。(问题是散弹枪问题,有些问题已经得到了客观答案,这些问题已经得到了回答。)在那一点上,我对争论的痕迹变得很敏感,并且您与Pavel达成的协议认为这是C ++的缺点。Namespace对于类或名称空间为什么很重要,我没有问题。只是别无所求,可以想象可能会在语法上引发一场语言大战。
David Thornley 2010年

Answers:


85

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

实际上,您在“重新打开”命名空间以声明新实体。如果该类Class后来被定义为不同名称空间的成员-这是一个完全不同的类,与您在此处声明的那个类无关。

一旦定义了预声明的类,就无需再次“重新打开”名称空间。您可以在全局名称空间(或包含的任何名称空间Namespace)中将其定义为

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

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


10
@STingRaySC:向前声明嵌套类的唯一方法是将声明放入封闭类的定义内。而且,在定义封闭类之前,确实没有办法向前声明嵌套类。
AnT 2010年

@STingRaySC:可以对嵌套类进行fwd声明-请参阅我的答案。
John Dibling 2010年

8
@John Dibling:嵌套类是在另一个类中声明的类。在名称空间中立即声明的类不是嵌套类。在您的答案中没有任何关于感性类的信息。
AnT 2010年

198

您得到正确的答案,让我尝试重新措词:

class Namespace::Class;

为什么我必须这样做?

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

...好,编译器。找到名为Namespace的名称空间,在其中引用名为Class的类。

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

namespace Namespace
{
};

class Namespace::Class;

它仍然不起作用,因为您不能从该名称空间外部声明该名称空间中的类。您必须位于名称空间中。

因此,实际上您可以在命名空间中向前声明一个类。只是这样做:

namespace Namespace
{
    class Class;
};

39
所有其他答案都使我感到困惑,但这是“您不能从该名称空间外部声明该名称空间中的类。您必须在该名称空间中。” 要记住的提示非常有用。
2013年

22

我想是出于同样的原因,您不能像这样一口气声明嵌套的名称空间:

namespace Company::Communications::Sockets {
}

并且您必须这样做:

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

1
这实际上不是答案,不能解释为什么不能这样做。
StarPilot

6
这是一个节省了我大量时间的答案
Kadir Erdem Demir

17
C ++ 17添加了这一点。
rparolin

在这里,您知道所有都是命名空间。但是对于Company :: Communications :: Socket类,您不知道Communications是命名空间还是类(其中socket是嵌套类)。
Lothar

11

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

namespace Namespace {
  class Class;
}

要么

class Namespace {
public:
  class Class;
};


6
我认为这是最好的答案之一,因为它回答了为什么编译器本身不容易确定的原因。
Devolus '19

1

关于禁止这样做的理由有很多很好的答案。我只想提供无聊的standardese条款,明确禁止使用它。这对于C ++ 17(n4659)成立。

有问题的段落是[class.name] / 2

仅由class-key标识符组成的声明;是在当前作用域中对该名称的重新声明,或者是将该标识符作为类名称的前向声明。它将类名引入当前范围。

上面定义了构成前向声明(或类的重新声明)的内容。本质上,它必须是一个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它不是标识符。这是一个完全合格的名称,有些不同。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.