web-dev-qa-db-ja.com

C ++フレンドクラスが他の名前空間でのみ前方宣言を必要とするのはなぜですか?

クラスFがクラスG(グローバルネームスペース内)およびC(ネームスペースA)のフレンドである必要があるとします。

  • A::Cと友達になるには、Fを前方宣言する必要があります。
  • Gと友だちになるためには、Fの前方宣言は必要ありません。
  • 同様に、クラスA::BFは、前方宣言なしでA::Cとフレンドになることができます

次のコードはこれを示し、GCC 4.5、VC++ 10、および少なくとも1つの他のコンパイラでコンパイルされます。

class G {
    friend class F;
    int g;
};

// without this forward declaration, F can't be friend to A::C
class F;

namespace A {

class C {
    friend class ::F;
    friend class BF;
    int c;
};

class BF {
public:
    BF() { c.c = 2; }
private:
    C c;
};

} // namespace A

class F {
public:
    F() { g.g = 3; c.c = 2; }
private:
    G g;
    A::C c;
};

int main()
{
    F f;
}

私にはこれは矛盾しているようです。これには理由がありますか、それとも単なる標準の設計上の決定ですか?

47
pesche

_C++_標準ISO/IEC 14882:2003(E)

7.3.1.2名前空間メンバーの定義

段落3

名前空間で最初に宣言されたすべての名前はその名前空間のメンバーです。非ローカルクラスのフレンド宣言が最初にクラスまたは関数を宣言する場合(これは、クラスまたは関数の名前が修飾されていないことを意味します)、フレンドクラスまたは関数は、最も内側の外側の名前空間のメンバーです。

_// Assume f and g have not yet been defined.
void h(int);
template <class T> void f2(T);
namespace A {
   class X {
   friend void f(X);  //  A::f(X) is a friend
      class Y {
         friend void g();  //  A::g is a friend
         friend void h(int);  //  A::h is a friend
         //  ::h not considered
         friend void f2<>(int);  //  ::f2<>(int) is a friend
      };
   };
   //  A::f, A::g and A::h are not visible here
   X x;
   void g() { f(x); }  // definition of A::g
   void f(X) { /* ... */}  // definition of A::f
   void h(int) { /* ... */ }  // definition of A::h
   //  A::f, A::g and A::h are visible here and known to be friends
}
_

_friend class BF;_は、グローバル名前空間ではなく名前空間Aでの_A::BF_の宣言です。この新しい宣言を回避するには、グローバル事前宣言が必要です。

45
Alexey Malistov

サンプルから次の3つのコード行を考慮してみましょう。

1. friend class F; // it creates "friend declaration", (that's not the same as ordinary forward declaration

2. class F; // without this forward declaration, F can't be friend to A::C <-- this is ordinary forward declaration

3. friend class ::F; // this is qualified lookup (because of ::), so it needs previous declaration, which you provide in line 2.

7.3.1.2項のC++標準、ポイント3(ネームスペースメンバーの定義)と言う:

フレンド宣言だけでは、名前を非修飾ルックアップ(3.4.1)または修飾ルックアップ(3.4.3)に表示しません。 [注:名前空間スコープで一致する宣言が提供されている場合(フレンドシップを付与するクラス定義の前または後)、フレンドの名前はそのネームスペースに表示されます。 —エンドノート]

そして、2行目は、標準が要求するものに正確に従います。

すべての混乱は、「フレンド宣言」がweakであるためです。今後の使用のために、確実なフォワード宣言を提供する必要があります。

namespace {}ブロックの内部にいる場合、グローバル名前空間で何かを宣言できるようにしても意味がないためです。 friend class BF;が機能するのは、暗黙の前方宣言のように機能するためです。

2
Billy ONeal