web-dev-qa-db-ja.com

二重コロンを使用して名前空間のクラスを前方宣言できないのはなぜですか?

class Namespace::Class;

なぜこれをしなければならないのですか?:

namespace Namespace {
    class Class;
}

VC++ 8.0を使用すると、コンパイラは次を発行します。

エラーC2653: 'Namespace':クラスまたは名前空間の名前ではありません

ここでの問題は、コンパイラがNamespaceがクラスなのか名前空間なのかを判断できないことだと思いますか?しかし、これは単なる前方宣言なので、なぜこれが重要なのでしょうか?

ある名前空間で定義されたクラスを前方宣言する別の方法はありますか?上記の構文は、私が名前空間を「再オープン」し、その定義を拡張しているように感じます。 ClassNamespaceで実際に定義されていない場合はどうなりますか?これはある時点でエラーになりますか?

154
user123456

できないから。 C++言語では、完全修飾名はexisting(つまり、以前に宣言された)エンティティを参照するためにのみ使用されます。 newエンティティの導入には使用できません。

そして、あなたはare実際に名前空間を「再オープン」して新しいエンティティを宣言します。クラスClassが後で別のネームスペースのメンバーとして定義されている場合-ここで宣言したクラスとはまったく関係のない完全に異なるクラスです。

definingの事前宣言されたクラスのポイントに達したら、名前空間を再度「開く」必要はありません。次のように、グローバル名前空間(またはNamespaceを囲む名前空間)で定義できます。

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

名前空間Namespaceで既に宣言されているエンティティを参照しているため、修飾名Namespace::Classを使用できます。

82
AnT

正解が得られました。言い替えてみましょう。

class Namespace::Class;

なぜこれをしなければならないのですか?

Namespace::Classという用語がコンパイラに指示しているため、これを行う必要があります。

... OK、コンパイラ。 Namespaceという名前の名前空間を探しに行き、その中でClassという名前のクラスを参照します。

しかし、コンパイラはNamespaceという名前の名前空間を知らないため、あなたが何を話しているのかを知りません。次のように、Namespaceという名前の名前空間があったとしても:

namespace Namespace
{
};

class Namespace::Class;

その名前空間の外部から名前空間内のクラスを宣言することはできないため、まだ機能しません。名前空間にいる必要があります。

したがって、名前空間内でクラスを実際に前方宣言できます。これを行うだけです:

namespace Namespace
{
    class Class;
};
188
John Dibling

ネストされた名前空間を次のように一度に宣言できないのと同じ理由によると思います。

namespace Company::Communications::Sockets {
}

あなたはこれをしなければなりません:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}
21
Igor Zevaka

前方宣言された変数の型が実際に何であるかは明らかではありません。前方宣言class Namespace::Class;は、

namespace Namespace {
  class Class;
}

または

class Namespace {
public:
  class Class;
};
7
Martin G

それを拒否することに関係する理論的根拠については、多くの優れた答えがあります。明確に禁止している退屈な標準条項を提供したいだけです。これは、C++ 17(n4659)にも当てはまります。

問題の段落は [class.name]/2

class-key identifierのみで構成される宣言。現在のスコープ内の名前の再宣言、またはクラス名としての識別子の前方宣言です。クラス名を現在のスコープに導入します。

上記は、前方宣言(またはクラスの再宣言)を構​​成するものを定義します。基本的に、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が有効な前方宣言から除外されます。これは完全に修飾された名前で、別の名前です。

0
StoryTeller