名前空間
変種
操作

フレンド宣言

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
アクセス指定子
friend指定子

クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

フレンド宣言は、クラス本体に現れ、フレンド宣言が現れるクラスのprivateおよびprotectedメンバーへのアクセス権を関数または別のクラスに付与します。

目次

[編集] 構文

friend function-declaration (1)
friend function-definition (2)
friend elaborated-type-specifier ; (3) (C++26まで)
friend simple-type-specifier ;

friend typename-specifier ;

(4) (C++11以降)
(C++26まで)
friend friend-type-specifier-list ; (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-specifierelaborated-type-specifier、およびtypename-specifier の、空でないカンマ区切りのリスト。各指定子は省略記号(...)で続けることができます。

[編集] 概要

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) (非ローカルクラス定義でのみ許可)非メンバ関数を定義し、同時にこのクラスのフレンドにします。そのような非メンバ関数は常にインラインです(名前付きモジュールにアタッチされている場合を除く)。(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によって名前が付けられます。名前付き型がクラス型でない場合、このフレンド宣言は無視されます。この宣言は新しい型を前方宣言しません。
5) friend-type-specifier-list内のすべてのクラスをこのクラスのフレンドとして指定します。これは、フレンドのメンバ宣言と定義がこのクラスのprivateおよびprotectedメンバーにアクセスでき、またフレンドがこのクラスのprivateおよびprotectedメンバーから継承できることを意味します。名前付き型がクラス型でない場合、このフレンド宣言では無視されます。
friend-type-specifier-list内の各指定子は、指定子が省略記号で終わっていない場合はクラスを名前付けし、そうでない場合はパック展開が適用されます。
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; // every B<T> is a friend of A
 
    template<typename T>
    friend void f(T) {} // every f<T> is a friend of A
};

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

template<class T>
class A {};      // primary
 
template<class T>
class A<T*> {};  // partial
 
template<>
class A<int> {}; // full
 
class X
{
    template<class T>
    friend class A<T*>;  // Error
 
    friend class A<int>; // OK
};

フレンド宣言が関数テンプレートの完全特殊化を参照する場合、キーワード inline(C++11以降)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); // error: default args not allowed
};

テンプレートフレンド宣言は、クラステンプレートAのメンバを名前付けできます。これはメンバ関数またはメンバ型(型は要素化型指定子を使用する必要がある)のいずれかです。このような宣言は、ネスト名指定子の最後のコンポーネント(最後の::の左側の名前)が単純テンプレートID(山括弧内の引数リストに続くテンプレート名)であり、クラステンプレートを名前付けしている場合にのみ有効です。このようなテンプレートフレンド宣言のテンプレートパラメータは、単純テンプレートIDから推論可能である必要があります。

この場合、Aの任意の特殊化またはAの部分特殊化のメンバがフレンドになります。これには、プライマリテンプレートAまたはAの部分特殊化のインスタンス化は含まれません。唯一の要件は、その特殊化からのAのテンプレートパラメータの推論が成功し、推論されたテンプレート引数をフレンド宣言に代入すると、特殊化のメンバの有効な再宣言となる宣言が生成されることです。

// primary template
template<class T>
struct A
{ 
    struct B {};
 
    void f();
 
    struct D { void g(); };
 
    T h();
 
    template<T U>
    T i();
};
 
// full specialization
template<>
struct A<int>
{
    struct B {};
 
    int f();
 
    struct D { void g(); };
 
    template<int U>
    int i();
};
 
// another full specialization
template<>
struct A<float*>
{
    int *h();
};
 
// the non-template class granting friendship to members of class template A
class X
{
    template<class T>
    friend struct A<T>::B; // all A<T>::B are friends, including A<int>::B
 
    template<class T>
    friend void A<T>::f(); // A<int>::f() is not a friend because its signature
                           // does not match, but e.g. A<char>::f() is a friend
 
//  template<class T>
//  friend void A<T>::D::g(); // ill-formed, the last part of the nested-name-specifier,
//                            // D in A<T>::D::, is not simple-template-id
 
    template<class T>
    friend int* A<T*>::h(); // all A<T*>::h are friends:
                            // A<float*>::h(), A<int*>::h(), etc
 
    template<class T> 
    template<T U>       // all instantiations of A<T>::i() and A<int>::i() are friends, 
    friend T A<T>::i(); // and thereby all specializations of those function templates
};

デフォルトテンプレート引数は、宣言が定義であり、かつこの翻訳単位にこの関数テンプレートの他の宣言が存在しない場合にのみ、テンプレートフレンド宣言で許可されます。

(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;
 
    // generates a non-template operator<< for this T
    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; // forward declare to make function declaration possible
 
template<typename T> // declaration
std::ostream& operator<<(std::ostream&, const Foo<T>&);
 
template<typename T>
class Foo
{
public:
    Foo(const T& val) : data(val) {}
private:
    T data;
 
    // refers to a full specialization for this particular T 
    friend std::ostream& operator<< <> (std::ostream&, const Foo&);
 
    // note: this relies on template argument deduction in declarations
    // can also specify the template argument with operator<< <T>"
};
 
// definition
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以降) 関数または関数テンプレートがフレンド宣言で宣言され、対応する非フレンド宣言が到達可能な場合、その名前は、その先行宣言から決定されるリンケージを持ちます。

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

[編集] 注記

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

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

アクセス指定子はフレンド宣言の意味に影響しません(private:またはpublic:セクションに現れることができ、違いはありません)。

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

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

class F {};
 
int f();
 
int main()
{
    extern int g();
 
    class Local // Local class in the main() function
    {
        friend int f(); // Error, no such function declared in main()
        friend int g(); // OK, there is a declaration for g in main()
        friend class F; // friends a local F (defined later)
        friend class ::F; // friends the global F
    };
 
    class F {}; // local F
}

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

機能テストマクロ 規格 機能
__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のフレンドクラスは、プライベートまたは
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 Friends [class.friend]
  • 13.7.5 Friends [temp.friend]
  • C++20 standard (ISO/IEC 14882:2020)
  • 11.9.3 Friends [class.friend]
  • 13.7.4 Friends [temp.friend]
  • C++17 standard (ISO/IEC 14882:2017)
  • 14.3 Friends [class.friend]
  • 17.5.4 Friends [temp.friend]
  • C++14 standard (ISO/IEC 14882:2014)
  • 11.3 Friends [class.friend]
  • 14.5.4 Friends [temp.friend]
  • C++11 standard (ISO/IEC 14882:2011)
  • 11.3 Friends [class.friend]
  • 14.5.4 Friends [temp.friend]
  • C++98 標準 (ISO/IEC 14882:1998)
  • 11.3 Friends [class.friend]
  • 14.5.3 Friends [temp.friend]

[編集] 関連項目

クラス型 複数のデータメンバーを持つ型を定義します。[編集]
アクセス指定子 クラスメンバーの可視性を定義します。[編集]
English 日本語 中文(简体) 中文(繁體)