Namespaces
Variants

Friend declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Access specifiers
friend specifier

Class-specific function properties
Special member functions
Templates
Miscellaneous

friend宣言は クラス本体 内に現れ、friend宣言が現れるクラスのprivateおよびprotectedメンバーに関数または他のクラスがアクセスすることを許可します。

目次

構文

friend 関数宣言 (1)
friend 関数定義 (2)
friend 詳細型指定子 ; (3) (C++26まで)
friend 単純型指定子 ;

friend typename指定子 ;

(4) (C++11以降)
(C++26まで)
friend フレンド型指定子リスト ; (5) (C++26以降)
1,2) 関数のフレンド宣言。
3-5) クラスのフレンド宣言。
function-declaration - 関数宣言
function-definition - 関数定義
elaborated-type-specifier - 詳細型指定子
simple-type-specifier - 単純型指定子
typename-specifier - キーワード typename に続く修飾付き識別子、または修飾付き 単純テンプレート識別子
friend-type-specifier-list - 空でないカンマ区切りの simple-type-specifier elaborated-type-specifier 、および typename-specifier のリスト。各指定子には省略記号( ... )を続けることができる

説明

1) このクラスのフレンドとして1つまたは複数の関数を指定します:
class Y
{
    int data; // private member
    // the non-member function operator<< will have access to Y's private members
    friend std::ostream& operator<<(std::ostream& out, const Y& o);
    friend char* X::foo(int); // members of other classes can be friends too
    friend X::X(char), X::~X(); // constructors and destructors can be friends
};
// friend declaration does not declare a member function
// this operator<< still needs to be defined, as a non-member
std::ostream& operator<<(std::ostream& out, const Y& y)
{
    return out << y.data; // can access private member Y::data
}
2) (非 local クラス定義でのみ許可) 非メンバー関数を定義し、同時にこのクラスのフレンド関数とする。このような非メンバー関数は常に inline であり、 名前付きモジュール に所属する場合を除く (C++20以降)
class X
{
    int a;
    friend void friend_set(X& p, int i)
    {
        p.a = i; // this is a non-member function
    }
public:
    void member_set(int i)
    {
        a = i; // this is a member function
    }
};
3,4) このクラスのフレンドとしてクラスを指定します。これは、フレンドのメンバー宣言と定義がこのクラスのprivateおよびprotectedメンバーにアクセスできること、またフレンドがこのクラスのprivateおよびprotectedメンバーから継承できることを意味します。
3) クラスは elaborated-type-specifier によって命名されます。このフレンド宣言で使用されるクラス名は、事前に宣言されている必要はありません。
4) クラスは simple-type-specifier または typename-specifier によって命名される。指定された型がクラス型でない場合、このfriend宣言は無視される。この宣言は新しい型を前方宣言しない。
5) このクラスのすべてのクラスを friend-type-specifier-list で指定されたフレンドとして宣言します。これは、フレンドのメンバー宣言と定義がこのクラスのprivateおよびprotectedメンバーにアクセスできることを意味し、またフレンドがこのクラスのprivateおよびprotectedメンバーから継承できることも意味します。指定された型がクラス型でない場合、このフレンド宣言では無視されます。
各指定子が friend-type-specifier-list 内にある場合、指定子が省略記号に続かない場合はクラスを指定し、それ以外の場合は pack expansion が適用されます。
class Y {};
class A
{
    int data; // private data member
    class B {}; // private nested type
    enum { a = 100 }; // private enumerator
    friend class X; // friend class forward declaration (elaborated class specifier)
    friend Y; // friend class declaration (simple type specifier) (since C++11)
    // the two friend declarations above can be merged since C++26:
    // friend class X, Y;
};
class X : A::B // OK: A::B accessible to friend
{
    A::B mx; // OK: A::B accessible to member of friend
    class Y
    {
        A::B my; // OK: A::B accessible to nested member of friend
    };
    int v[A::a]; // OK: A::a accessible to member of friend
};

テンプレートフレンド

関数テンプレートとクラステンプレートの宣言は、非ローカルクラスまたはクラステンプレート内で friend 指定子と共に記述できます(ただし、フレンドシップを付与するクラスまたはクラステンプレート内で定義できるのは関数テンプレートのみです)。この場合、テンプレートのすべての特殊化がフレンドとなり、それが暗黙的にインスタンス化されたもの、部分的に特殊化されたもの、または明示的に特殊化されたものかどうかを問いません。

class A
{
    template<typename T>
    friend class B; // すべてのB<T>はAのフレンド
    template<typename T>
    friend void 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>
    friend class A<T*>;  // エラー
    friend class A<int>; // OK
};

フレンド宣言が関数テンプレートの完全特殊化を参照する場合、キーワード inline , constexpr (C++11以降) , consteval (C++20以降) およびデフォルト引数は使用できません:

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

テンプレートのフレンド宣言は、クラステンプレートAのメンバー(メンバー関数またはメンバー型)を指定できます(型は elaborated-type-specifier を使用する必要があります)。このような宣言は、そのネストされた名前指定子の最後の構成要素(最後の :: の左側の名前)がクラステンプレートを指定するsimple-template-id(テンプレート名と山括弧内の引数リスト)である場合にのみ適切な形式となります。このようなテンプレートフレンド宣言のテンプレートパラメータは、simple-template-idから推論可能でなければなりません。

この場合、Aの任意の特殊化またはAの部分特殊化のメンバーはフレンドとなります。これは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>
    friend struct A<T>::B; // すべてのA<T>::Bはフレンド、A<int>::Bも含む
    template<class T>
    friend void A<T>::f(); // A<int>::f()はシグネチャが一致しないためフレンドではないが、
                           // 例えばA<char>::f()はフレンドである
//  template<class T>
//  friend void A<T>::D::g(); // 不適格、ネストされた名前指定子の最後の部分
//                            // A<T>::D:: のDが単純テンプレートIDではない
    template<class T>
    friend int* 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以降)

テンプレートフレンド演算子

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

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

#include <iostream>
template<typename T>
class Foo
{
public:
    Foo(const T& val) : data(val) {}
private:
    T data;
    // このTに対する非テンプレートoperator<<を生成
    friend std::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;
    // この特定のTに対する完全特殊化を参照
    friend std::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';
}

リンケージ

ストレージクラス指定子 はフレンド宣言では使用できません。

関数または関数テンプレートがフレンド宣言で最初に宣言および定義され、かつ外側のクラスが エクスポート宣言 内で定義されている場合、その名前は外側のクラスの名前と同じリンケージを持つ。

(C++20以降)

もし (C++20まで) それ以外の場合、 (C++20以降) 関数または関数テンプレートがフレンド宣言で宣言され、かつ 対応する非フレンド宣言 が到達可能である場合、その名前は先行する宣言から決定されたリンケージを持つ。

それ以外の場合、フレンド宣言によって導入された名前のリンケージは通常通り決定されます。

注記

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

フレンドシップは継承されません(あなたの友人の子供はあなたの友人ではなく、あなたの友人もあなたの子供の友人ではありません)。

Access specifiers はフレンド宣言の意味に影響を与えません(それらは private : セクションまたは public : セクションに現れても違いはありません)。

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

ローカルクラスが修飾なしの関数またはクラスをフレンドとして宣言する場合、最も内側の非クラススコープ内の関数とクラスのみが 検索され 、グローバル関数は検索されません:

class F {};
int f();
int main()
{
    extern int g();
    class Local // main()関数内のローカルクラス
    {
        friend int f(); // エラー:main()内で宣言されたそのような関数は存在しない
        friend int g(); // OK:main()内にgの宣言が存在する
        friend class F; // ローカルのF(後で定義)をフレンドとする
        friend class ::F; // グローバルスコープのFをフレンドとする
    };
    class F {}; // ローカルのF
}

クラスまたはクラステンプレート X 内のfriend宣言で最初に宣言された名前は、 X の最も内側の外側の名前空間のメンバーになりますが、名前空間スコープでの一致する宣言が提供されない限り、ルックアップには見えません( X を考慮する実引数依存ルックアップを除く)。詳細は namespaces を参照してください。

機能テストマクロ 規格 機能
__cpp_variadic_friend 202403L (C++26) 可変長フレンド宣言

キーワード

friend

ストリーム挿入演算子と抽出演算子は、非メンバーフレンド関数として宣言されることが多い:

#include <iostream>
#include <sstream>
class MyClass
{
    int i;                   // friends have access to non-public, non-static
    static inline int id{6}; // and static (possibly inline) members
    friend std::ostream& operator<<(std::ostream& out, const MyClass&);
    friend std::istream& operator>>(std::istream& in, MyClass&);
    friend void change_id(int);
public:
    MyClass(int i = 0) : i(i) {}
};
std::ostream& operator<<(std::ostream& out, const MyClass& mc)
{
    return out << "MyClass::id = " << MyClass::id << "; i = " << mc.i;
}
std::istream& operator>>(std::istream& in, MyClass& mc)
{
    return in >> mc.i;
}
void change_id(int id) { MyClass::id = id; }
int main()
{
    MyClass mc(7);
    std::cout << mc << '\n';
//  mc.i = 333*2;  // error: i is a private member
    std::istringstream("100") >> mc;
    std::cout << mc << '\n';
//  MyClass::id = 222*3;  // error: id is a private member
    change_id(9);
    std::cout << mc << '\n';
}

出力:

MyClass::id = 6; i = 7
MyClass::id = 6; i = 100
MyClass::id = 9; i = 100

不具合報告

以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。

DR 適用対象 公開時の動作 正しい動作
CWG 45 C++98 T のフレンドクラス内にネストされたクラスのメンバーは
T への特別なアクセス権を持たない
ネストされたクラスは外側のクラスと
同じアクセス権を持つ
CWG 500 C++98 T のフレンドクラスは T のprivateまたはprotectedメンバーから
継承できないが、そのネストされたクラスは継承できる
両者ともそのようなメンバーから
継承可能
CWG 1439 C++98 非ローカルクラス内のフレンド宣言を対象とするルールが
テンプレート宣言をカバーしていなかった
カバー対象に含まれる
CWG 1477 C++98 クラスまたはクラステンプレート内のフレンド宣言で最初に宣言された
名前は、マッチする宣言が他の名前空間スコープで提供されている場合、
ルックアップで可視ではなかった
この場合でも
ルックアップで可視
CWG 1804 C++98 クラステンプレートのメンバーがフレンド指定された場合、
そのクラステンプレートの部分特殊化の特殊化における
対応するメンバーはフレンド指定されなかった
そのようなメンバーも
フレンドとなる
CWG 2379 C++11 関数テンプレートの完全特殊化を参照するフレンド宣言を
constexprで宣言できた
禁止
CWG 2588 C++98 フレンド宣言によって導入される名前のリンケージが不明確だった 明確化された

参考文献

  • C++23規格 (ISO/IEC 14882:2024):
  • 11.8.4 フレンド [class.friend]
  • 13.7.5 フレンド [temp.friend]
  • C++20標準 (ISO/IEC 14882:2020):
  • 11.9.3 フレンド [class.friend]
  • 13.7.4 フレンド [temp.friend]
  • C++17標準 (ISO/IEC 14882:2017):
  • 14.3 フレンド [class.friend]
  • 17.5.4 フレンド [temp.friend]
  • C++14標準 (ISO/IEC 14882:2014):
  • 11.3 フレンド [class.friend]
  • 14.5.4 フレンド [temp.friend]
  • C++11標準 (ISO/IEC 14882:2011):
  • 11.3 フレンド [class.friend]
  • 14.5.4 フレンド [temp.friend]
  • C++98標準 (ISO/IEC 14882:1998):
  • 11.3 フレンド [class.friend]
  • 14.5.3 フレンド [temp.friend]

関連項目

Class types 複数のデータメンバーを保持する型を定義します
Access specifiers クラスメンバーの可視性を定義します