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


164
class Namespace::Class;

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

namespace Namespace {
    class Class;
}

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

エラーC2653:「名前空間」:クラスまたは名前空間名ではありません

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

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


44
ここでのすべての回答に同意しないでください。これは単に言語の設計上のバグであると言います。彼らはもっとよく考えることができます。
Pavel Radzivilovsky、2010年

これは、これがC ++(これは主観的)で違法である理由の議論に向かう傾向があり、議論の余地があるように見えます。閉じる投票。
David Thornley、2010年

7
コンパイラは、クラス名の代わりに名前空間識別子でA::Bあることをどのようにして知っているはずAですか?
David R Tribble、2010年

@STingRaySC:議論は主観的であり、C ++がこれを行う理由が明確ではないため、推測しています。(この質問はショットガンの質問であり、客観的な回答を含むいくつかの質問は既に回答されています。)その時点で、私は議論の痕跡に敏感になり、Pavelとのあなたの同意はこれがC ++の誤った機能であることを認めます。Namespaceクラスと名前空間のどちらが重要であるのか、という質問には問題ありません。おそらく、構文をめぐる言語の炎上戦争が始まる可能性のヒントに近づかないでください。
David Thornley

回答:


85

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

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

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

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

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


10
@STingRaySC:ネストされたクラスを前方宣言する唯一の方法は、外側のクラスの定義内に宣言を置くことです。また、入れ子のクラスを定義する前に、ネストされたクラスを前方宣言する方法はありません。
AnT、2013年

@STingRaySC:ネストされたクラスはfwd宣言できます-私の回答を参照してください。
John Dibling 2010年

8
@John Dibling:ネストされたクラスは、別のクラス内で宣言されたクラスです。名前空間のすぐ内側で宣言されたクラスは、ネストされたクラスではありません。回答に感覚クラスについては何もありません。
AnT

198

あなたは正しい答えを得ています、私に言い直してみます:

class Namespace::Class;

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

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

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

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

namespace Namespace
{
};

class Namespace::Class;

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

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

namespace Namespace
{
    class Class;
};

39
他のすべての答えは私を混乱させましたが、これは「名前空間の外から名前空間内のクラスを宣言することはできません。名前空間にいる必要があります。」覚えておくと非常に役立つヒントでした。
ダッシュ

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 2017

12

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

namespace Namespace {
  class Class;
}

または

class Namespace {
public:
  class Class;
};


6
これは最良の答えの1つだと思います。なぜなら、これがコンパイラー自体では簡単に決定できない理由を答えるからです。
Devolus

1

それを拒否することの論理的根拠については、多くの優れた答えがあります。特に退屈な標準的な条項を具体的に禁止したいだけです。これは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は有効な前方宣言ではありません。これは完全修飾名であり、別の名前です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.