2010-12-20 10 views
41

G(グローバルネームスペース内)とC(ネームスペースA)のクラスに友人になるクラスFがあるとします。C++フレンドクラスは他のネームスペースでのみ前方宣言を必要とするのはなぜですか?

  • A::Cに友人になるためには、Fを前方宣言する必要があります。
  • Gの友人になるためには、Fというフォワード宣言は必要ありません。
  • 同様に、クラスA::BFは前方宣言せずに

A::Cに友人もでき、次のコードは、このことを示しており、GCC 4.5、VC++ 10と少なくとも一つの他のコンパイラでコンパイルして。

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; 
} 

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

答えて

40

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

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

条第3項

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

// 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 
} 

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

+0

+1。それは書くために十分ではない理由を –

+9

合理的な人は、「F :: 'フレンドクラス;'」頼むかもしれない(OPのコードに示すように)、これを明示的グローバル名前空間に 'F'を押します。答えは「修飾IDは決して新しい名前を宣言しません」という行に沿っていると思いますが、この点について標準が何を言っているのか正確には分かりません。それ@Zack – zwol

+0

は、あまりにも、私の最初の質問だった:なぜ私は別の名前空間内のクラスを移動すると、前方宣言を追加する必要がありますか?しかし、このトピックについて既にいくつかの議論があります(例:http://stackoverflow.com/questions/2059665/、http://stackoverflow.com/questions/1368642/)。同じ範囲で前方宣言を必要としないことは、標準の作成者によって認められた利便性であると思われる。 – pesche

1

namespace {}ブロック内にある場合は、グローバル名前空間で何かを宣言できるとは限りません。 friend class BF;の理由は、それが暗黙のフォワード宣言のように機能するためです。

3

のアカウントにあなたのサンプルからこれらの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. 

C++段落7.3.1.2で標準、ポイント3(名前空間のメンバーの定義)は言う:

友人宣言だけでは、名前が の非修飾ルックアップ(3.4.1)または修飾ルックアップ(3.4.3)に表示されません。[注:一致 宣言は(友情を付与 クラス定義の前または後のいずれか)名前空間の範囲に設けられている場合は友人の 名前は、その名前空間に表示されます。末端ノート]

そして、2行目は、標準が必要とまさに続きます。

「友人宣言」が弱いであるため、すべての混乱は今後の使用のために順方向の宣言を提供する必要があります。実際の標準的な参考のため

関連する問題