フレンドのスコープ

C++フレンド宣言で最初に導入されるフレンド関数またはフレンド・クラスの名前は、フレンド関係を認可する クラス (囲みクラスとも呼ばれます) のスコープ内にはなく、フレンド関係を認可する クラスのメンバーでもありません。

フレンド宣言で最初に導入された関数の名前は、囲みクラスが含まれている最初の非クラス・スコープのスコープ内にあります。 フレンド宣言で与えられる関数の本体は、クラス内で定義されるメンバー関数と同じ方法で処理されます。 定義の処理は、最外部の囲みクラスの終わりまで開始されません。 さらに、関数定義の本体内の修飾されない名前による検索は、その関数定義が入っているクラスから始められます。

フレンド宣言で最初に宣言されるクラスは、extern 宣言と同等です。次に例を示します。

class B {};
class A
{
      friend class B; // global class B is a friend of A
};

フレンド・クラスの名前が、フレンド宣言よりも前に導入されている場合、コンパイラーは、その フレンド・クラスの名前と一致するクラス名の検索を、フレンド宣言のスコープの先頭から開始します。 ネスト・クラスの宣言の後に同じ名前のフレンド・クラスの宣言が続いている場合、そのネスト・クラスは、 囲みクラスのフレンドです。

フレンド・クラス名のスコープは、最初の非クラスの囲みスコープです。 次に例を示します。

class A {
   class B { // arbitrary nested class definitions
      friend class C;
   };
};

は、以下と同等です。

class C;
class A {
   class B { // arbitrary nested class definitions
      friend class C;
   };
};

フレンド関数が別のクラスのメンバーである場合には、スコープ・レゾリューション演算子 (::) を 使用する必要があります。 次に例を示します。

class A {
public:
  int f() { }
};
 
class B {
  friend int A::f();
};

基底クラスのフレンドは、その基底クラスから派生したクラスには、継承されません。 次の例は、このことを示しています。

class A {
  friend class B;
  int a;
};
 
class B { };
 
class C : public B {
  void f(A* p) {
//    p->a = 2;
  }
};
CA のフレンドから継承していますが、クラス C が クラス A のフレンドではないので、コンパイラーは、ステートメント p->a = 2 を 許可しません。

フレンド関係は、移行できません。 次の例は、このことを示しています。

class A {
  friend class B;
  int a;
};
 
class B {
  friend class C;
};
 
class C {
  void f(A* p) {
//    p->a = 2;
  }
};
CA のフレンドのフレンドですが、クラス C が クラス A のフレンドではないので、コンパイラーは、ステートメント p->a = 2 を 許可しません。

ローカル・クラスにフレンドを宣言し、フレンドの名前が非修飾である場合、コンパイラーは、最も内側にある 囲みの非クラス・スコープ内でのみ名前を検索します。 関数を宣言してから、ローカル・スコープのフレンドとして関数を宣言する必要があります。 クラスでこれを実行する必要はありません。 しかし、フレンド・クラスの宣言は、囲みスコープ内の、同じ名前のクラスを隠します。 次の例は、このことを示しています。

class X { };
void a();
 
void f() {
  class Y { };
  void b();
  class A {
    friend class X;
    friend class Y;
    friend class Z;
//    friend void a();
    friend void b();
//    friend void c();
  };
  ::X moocow;
//  X moocow2;
}

上記の例では、コンパイラーは、次のステートメントを認めます。

コンパイラーは、次のステートメントを許可しません。

関連参照

IBM Copyright 2003