名前空間
変種
操作

フレンド宣言

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定(C++20未満)
noexcept 指定子(C++11)
例外
名前空間
指定子
decltype(C++11)
auto(C++11)
alignas(C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr(C++11)
ユーザ定義(C++11)
ユーティリティ
属性(C++11)
typedef 宣言
型エイリアス宣言(C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
アクセス指定子
friend 指定子
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
 

フレンド宣言はクラス本体内に現れ、そのクラスの private または protected メンバへのアクセスを他のクラスまたは関数に許可します。

目次

[編集]構文

friendfunction-declaration (1)
friendfunction-definition (2)
friendelaborated-class-specifier; (3)
friendsimple-type-specifier;

friendtypename-specifier;

(4) (C++11以上)

[編集]説明

1) ひとつまたは複数の関数をこのクラスのフレンドとして指定します。
class Y {int data;// プライベートメンバ。   // 非メンバ関数 operator<< から Y のプライベートメンバへのアクセスを許可します。friendstd::ostream& operator<<(std::ostream& out, const Y& o);   // 他のクラスのメンバともフレンドになれます。friendchar* X::foo(int);   // コンストラクタやデストラクタもフレンドになれます。friend X::X(char), X::~X();};   // フレンド宣言はメンバ関数を宣言するわけではありません。// この operator<< は非メンバとして定義する必要があります。std::ostream& operator<<(std::ostream& out, const Y& y){return out << y.data;// プライベートメンバ Y::data にアクセスできます。}
2) (非ローカルクラスの定義でのみ使用できます。) 非メンバ関数を定義し、同時にそれをこのクラスのフレンドにします。 このような非メンバ関数は常に inline です。
class X {int a;friendvoid friend_set(X& p, int i){// これは非メンバ関数です。 p.a= i;}public:void member_set(int i){// これはメンバ関数です。 a = i;}};
3)elaborated-class-specifier (複雑型指定子 を参照) によって表されるクラス、構造体、または共用体をこのクラスのフレンドとして指定します。 これはフレンドのメンバ宣言および定義がこのクラスの private および protected メンバにアクセスでき、またフレンドがこのクラスの private および protected メンバから継承できることを意味します。 このフレンド宣言で使用されるクラスの名前は事前に宣言されている必要はありません。
4)simple-type-specifier または typename-specifier によって表される型がクラス、構造体、または共用体 (またはそれらの cv 修飾された型) であれば、その型をこのクラスのフレンドとして宣言します。 そうでなければ、その friend 宣言は無視されます。 この宣言は新しい型を前方宣言しません。
class Y {};class A {int data;// プライベートデータメンバ。class B {};// プライベートなネストした型。enum{ a =100};// プライベートな列挙子。friendclass X;// フレンドクラスの前方宣言 (複雑型指定子)。friend Y;// フレンドクラスの宣言 (単純型指定子) (C++11以上)。};   class X : A::B{// OK、フレンドは A::B にアクセスできます。 A::B mx;// OK、フレンドのメンバは A::B にアクセスできます。class Y { A::B my;// OK、フレンドのネストしたメンバは A::B にアクセスできます。};int v[A::a];// OK、フレンドのメンバは A::a にアクセスできます。};

[編集]ノート

フレンドは推移的ではありません (あなたの友達の友達は、あなたの友達ではありません)。

フレンドは継承されません (あなたの友達の子供は、あなたの友達ではありません)。

フレンド関数の宣言では記憶域クラス指定子は使用できません。 フレンド宣言で定義される関数は外部リンケージを持ち、以前に定義された関数はそれが定義されたときのリンケージを維持します。

フレンド宣言ではアクセス指定子は効果がありません (フレンド宣言は private: セクションにも public: セクションにも書けますが、違いはありません)。

フレンドクラスの宣言で新しいクラスは定義できません (friendclass X {}; はエラーです)。

ローカルクラスが非修飾名の関数またはクラスをフレンドとして宣言したときは、グローバルな関数ではなく、最も内側の非クラススコープ内の関数およびクラスのみが名前探索されます。

class F {};int f();int main(){externint g();class Local {// main() 関数内のローカルクラス。friendint f();// エラー、 main() 内にそのような関数は宣言されていません。friendint g();// OK、 main() 内に g の宣言があります。friendclass F;// ローカルな F (後で定義されます) をフレンドにします。friendclass::F;// グローバルな F をフレンドにします。};class F {};// ローカルな F。}

クラスまたはクラステンプレート X 内のフレンド宣言で最初に宣言された名前は、 X の最も内側の囲っている名前空間のメンバになりますが、その名前空間スコープで一致する宣言が提供されない限り、 (X を考慮した実引数依存の名前探索除いて) 名前探索に対して可視ではありません。 詳細については名前空間を参照してください。

[編集]テンプレートフレンド

関数テンプレートおよびクラステンプレートはどちらも、任意の非ローカルクラスまたはクラステンプレート内に friend 指定子と共に現れることができます (ただしフレンドを許可するクラスまたはクラステンプレート内で定義できるのは関数テンプレートだけです)。 この場合、暗黙に実体化されたか部分特殊化されたか明示的に特殊化されたかに関わらず、そのテンプレートのすべての特殊化がフレンドとなります。

class A {template<typename T>friendclass B;// すべての B<T> が A のフレンドです。   template<typename T>friendvoid f(T){}// すべての f<T> が A のフレンドです。};

フレンド宣言は部分特殊化を参照することはできませんが、完全特殊化を参照することはできます。

template<class T>class A {};// プライマリテンプレート。template<class T>class A<T*>{};// 部分特殊化。template<>class A<int>{};// 完全特殊化。class X {template<class T>friendclass A<T*>;// エラー。friendclass A<int>;// OK。};

フレンド宣言が関数テンプレートの完全特殊化を参照するとき、キーワード inline およびデフォルト引数は使用できません。

template<class T>void f(int);template<>void f<int>(int);   class X {friendvoid f<int>(int x =1);// エラー、デフォルト引数は許されません。};

テンプレートフレンド宣言はクラステンプレート A のメンバを表すことができ、それはメンバ関数またはメンバ型のどちらにもできます (型は複雑型指定子を用いなければなりません)。 そのような宣言はその nested-name-specifier の最後の部分 (最後の :: の左の名前) がクラステンプレートを表す simple-template-id (テンプレート名に角括弧で囲った引数リストが続いたもの) である場合にのみ well-formed です。 そのようなテンプレートフレンド宣言のテンプレート引数は simple-template-id から推定できなければなりません。

この場合、 A のあらゆる特殊化のそのメンバがフレンドになります。 これはプライマリテンプレート A の実体化を発生させません。 唯一の要件は、その特殊化の A のテンプレート引数の推定が成功し、その推定したテンプレート引数のフレンド宣言への置き換えがその特殊化のメンバの有効な再宣言となるであろう宣言を生成することです。

// プライマリテンプレート。template<class T>struct A {struct B {};void f();struct D {void g();}; T h();template<T U> T i();};// 完全特殊化。template<>struct A<int>{struct B {};int f();struct D {void g();};template<int U>int i();};// 別の完全特殊化。template<>struct A<float*>{int*h();};// クラステンプレート A のメンバにフレンドを許可する非テンプレートクラス。class X {template<class T>friendstruct A<T>::B;// すべての A<T>::B (A<int>::B を含む) がフレンドです。    template<class T>friendvoid A<T>::f();// A<int>::f() はシグネチャが一致しないためフレンドではありません// が、例えば A<char>::f() はフレンドです。   // template<class T>// friend void A<T>::D::g(); // ill-formed、 nested-name-specifier の最後の部分// // (A<T>::D:: の D) が simple-template-id ではありません。   template<class T>friendint* A<T*>::h();// すべての A<T*>::h (A<float*>::h()、 A<int*>::h()など) がフレンドです。   template<class T>template<T U>// A<T>::i() のすべての実体化と A<int>::i() がフレンドであり、friend T A<T>::i();// そのためそれらの関数テンプレートのすべての特殊化がフレンドです。};

デフォルトテンプレート引数は、その宣言が定義であり、その関数テンプレートのそれ以外の宣言がこの翻訳単位内に現れない場合にのみ、テンプレートフレンド宣言に対して許されます。

(C++11以上)

[編集]テンプレートフレンド演算子

テンプレートフレンドの一般的なユースケースは、クラステンプレートに対して動作する非メンバの演算子オーバーロードの宣言です。 例えば、何らかのユーザ定義型 Foo<T> に対する operator<<(std::ostream&, const Foo<T>&) などです。

そのような演算子はクラスの本体内部で定義することができ、それぞれの T に対して別々の非テンプレート operator<< を生成してその非テンプレート operator<<Foo<T> のフレンドとする効果があります。

#include <iostream>   template<typename T>class Foo {public: Foo(const T& val): data(val){}private: T data;   // this の T に対して非テンプレート operator<< を生成します。friendstd::ostream& operator<<(std::ostream& os, const Foo& obj){return os << obj.data;}};   int main(){ Foo<double> obj(1.23);std::cout<< obj <<'\n';}

出力:

1.23

さもなければ、関数テンプレートをクラス本体より前にテンプレートとして宣言する必要があります。 この場合、 Foo<T> 内のフレンド宣言は、その T に対する operator<< の完全特殊化を参照します。

#include <iostream>   template<typename T>class Foo;// 関数宣言を可能とするための前方宣言。   template<typename T>// 宣言。std::ostream& operator<<(std::ostream&, const Foo<T>&);   template<typename T>class Foo {public: Foo(const T& val): data(val){}private: T data;   // this の特定の T に対する完全特殊化を参照します。friendstd::ostream& operator<<<>(std::ostream&, const Foo&);// ノート: この宣言はテンプレートの実引数推定を利用しています。// 「operator<< <T>」のようにテンプレート引数を指定することもできます。};   // 定義。template<typename T>std::ostream& operator<<(std::ostream& os, const Foo<T>& obj){return os << obj.data;}   int main(){ Foo<double> obj(1.23);std::cout<< obj <<'\n';}


[編集]

ストリーム挿入演算子および抽出演算子はしばしば非メンバのフレンドとして宣言されます。

#include <iostream>#include <sstream>   class MyClass {int i;   friendstd::ostream& operator<<(std::ostream& out, const MyClass& o);friendstd::istream& operator>>(std::istream& in, MyClass& o);public: MyClass(int i =0): i(i){}};   std::ostream& operator<<(std::ostream& out, const MyClass& mc){return out << mc.i;}   std::istream& operator>>(std::istream& in, MyClass& mc){return in >> mc.i;}   int main(){ MyClass mc(7);std::cout<< mc <<'\n';std::istringstream("100")>> mc;std::cout<< mc <<'\n';}

出力:

7 100

[編集]欠陥報告

以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。

DR 適用先 発行時の動作 正しい動作
CWG 45 C++98 members of a class nested in a friend class of T have no special access to T a nested class has the same access as the enclosing class
CWG 500 C++98 friend class of T cannot inherit from private or protected members of T, but its nested class can both can inherit from such members

[編集]参考文献

  • C++11 standard (ISO/IEC 14882:2011):
  • 11.3 Friends [class.friend]
  • 14.5.4 Friends [temp.friend]
  • C++98 standard (ISO/IEC 14882:1998):
  • 11.3 Friends [class.friend]
  • 14.5.3 Friends [temp.friend]

[編集]関連項目

クラス宣言
アクセス指定子
close