为什么我不能使用双冒号在命名空间中转发声明一个类?

时间:2010-01-13 19:41:01

标签: c++ namespaces

class Namespace::Class;

为什么我必须这样做?:

namespace Namespace {
    class Class;
}

使用VC ++ 8.0,编译器会发出:

  

错误C2653:'Namespace':不是类或命名空间名称

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

还有另一种方法来转发声明在某个命名空间中定义的类吗?上面的语法感觉就像我“重新打开”命名空间并扩展其定义。如果Class中实际未定义Namespace怎么办?这会在某个时候导致错误吗?

5 个答案:

答案 0 :(得分:184)

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

class Namespace::Class;

  

为什么我必须这样做?

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

  

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

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

namespace Namespace
{
};

class Namespace::Class;

它仍然无效,因为您无法在该命名空间外部声明命名空间中的类。你必须在名称空间中。

因此,您实际上可以在命名空间中转发声明一个类。就这样做:

namespace Namespace
{
    class Class;
};

答案 1 :(得分:82)

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

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

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

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

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

答案 2 :(得分:21)

我认为这是出于同样的原因,你不能像这样一次声明嵌套命名空间:

namespace Company::Communications::Sockets {
}

你必须这样做:

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

答案 3 :(得分:2)

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

namespace Namespace {
  class Class;
}

class Namespace {
public:
  class Class;
};

答案 4 :(得分:0)

关于禁止它的理由,有很多很好的答案。我只是想提供无聊的标准条款,具体禁止它。这适用于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不是标识符。这是一个完全合格的名字,不同的东西。