class Namespace::Class;
なぜこれをしなければならないのですか?:
namespace Namespace {
class Class;
}
VC++ 8.0を使用すると、コンパイラは次を発行します。
エラーC2653: 'Namespace':クラスまたは名前空間の名前ではありません
ここでの問題は、コンパイラがNamespace
がクラスなのか名前空間なのかを判断できないことだと思いますか?しかし、これは単なる前方宣言なので、なぜこれが重要なのでしょうか?
ある名前空間で定義されたクラスを前方宣言する別の方法はありますか?上記の構文は、私が名前空間を「再オープン」し、その定義を拡張しているように感じます。 Class
がNamespace
で実際に定義されていない場合はどうなりますか?これはある時点でエラーになりますか?
できないから。 C++言語では、完全修飾名はexisting(つまり、以前に宣言された)エンティティを参照するためにのみ使用されます。 newエンティティの導入には使用できません。
そして、あなたはare実際に名前空間を「再オープン」して新しいエンティティを宣言します。クラスClass
が後で別のネームスペースのメンバーとして定義されている場合-ここで宣言したクラスとはまったく関係のない完全に異なるクラスです。
definingの事前宣言されたクラスのポイントに達したら、名前空間を再度「開く」必要はありません。次のように、グローバル名前空間(またはNamespace
を囲む名前空間)で定義できます。
class Namespace::Class {
/* whatever */
};
名前空間Namespace
で既に宣言されているエンティティを参照しているため、修飾名Namespace::Class
を使用できます。
正解が得られました。言い替えてみましょう。
class Namespace::Class;
なぜこれをしなければならないのですか?
Namespace::Class
という用語がコンパイラに指示しているため、これを行う必要があります。
... OK、コンパイラ。 Namespaceという名前の名前空間を探しに行き、その中でClassという名前のクラスを参照します。
しかし、コンパイラはNamespace
という名前の名前空間を知らないため、あなたが何を話しているのかを知りません。次のように、Namespace
という名前の名前空間があったとしても:
namespace Namespace
{
};
class Namespace::Class;
その名前空間の外部から名前空間内のクラスを宣言することはできないため、まだ機能しません。名前空間にいる必要があります。
したがって、名前空間内でクラスを実際に前方宣言できます。これを行うだけです:
namespace Namespace
{
class Class;
};
ネストされた名前空間を次のように一度に宣言できないのと同じ理由によると思います。
namespace Company::Communications::Sockets {
}
あなたはこれをしなければなりません:
namespace Company {
namespace Communications {
namespace Sockets {
}
}
}
前方宣言された変数の型が実際に何であるかは明らかではありません。前方宣言class Namespace::Class;
は、
namespace Namespace {
class Class;
}
または
class Namespace {
public:
class Class;
};
それを拒否することに関係する理論的根拠については、多くの優れた答えがあります。明確に禁止している退屈な標準条項を提供したいだけです。これは、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
が有効な前方宣言から除外されます。これは完全に修飾された名前で、別の名前です。