Hidden Friends
クラスの定義内でfriend
として定義されている関数のことをHidden Friendsと呼ぶ。Hidden Friendsな関数は通常のメンバ関数やグローバル関数の様に呼ぶことは出来ず、ADLでのみ呼び出すことができる。
namespace NS {
struct C {
// メンバ関数
void mem_func() {}
// Hidden Friends
friend void hidden_friend(C& self) {}
};
// 普通の非メンバ関数
void free_func(C& c) {}
}
int main() {
NS::C c{};
// メンバ関数
c.mem_func(); // ok
mem_func(c); // ng
NS::mem_func(c); // ng
// Hidden friends
c.hidden_friend(); // ng
hidden_friend(c); // ok
NS::hidden_friend(c); // ng
// 普通の非メンバ関数
c.free_func(); // ng
free_func(c); // ok
NS::free_func(c); // ok
}
C++20以降の標準ライブラリでは一部のカスタマイゼーションポイントの関数や演算子オーバーロードがHidden Friendsとして定義される様になる。また、標準ライブラリの実装によってはHidden Friendsと規定されていない既存の演算子オーバーロードをHidden Friendsとして定義していることがある。
Hidden FriendsはADL経由でしか呼べず、名前空間スコープに露出しておらずクラスのインターフェースにも含まれないという性質から次の様な利点がある。
- 暗黙変換による意図しない関数呼び出しの防止
- 名前空間スコープ汚染の抑止
- クラスインターフェース増加の抑止
- (名前空間スコープに定義する時と比較して)オーバーロード候補の減少によるコンパイル時間の削減
- メンバとして定義することができない一部の演算子オーバーロードでは宣言を簡素化できる
Customization Point Object(CPO)とHidden Friends
あるクラスについてカスタマイゼーションポイントにアダプトする時には、そこで求められる名前でメンバ関数もしくは非メンバ関数を定義することが求められる。しかし、その関数がそのクラスのインターフェースの一部としてそぐわない事から、関数をHidden Friendsとして定義する事が好まれる場合がある。そのため、多くのCPOは引数型に対して同名のHidden Friends関数を探しに行くように定義されている。