🪪
Passkey idiom (パスキー・イディオム)
既に他の方が紹介されていますが、面白いなと思ったのでこちらでも紹介したいと思います。
Friendクラスを使って、他のクラスにプライベートメンバーを共有したいケースにおいて、共有するメンバーを限定できる方法です。例えば、以下のようなコードにおいて
class F {
friend class G;
int data_;
int data_really_private_; // こっちはGに公開したくない…
};
class G {
public:
int get_f_data(const F & f) const {
return f.data_;
}
};
以下のように、クラス自身にしか自身で特殊化したクラスを作成できないPasskeyというクラスを用意することで、そのPasskeyを要求するようなパブリックメンバー関数を用意することでアクセス制御が可能になります。クラス自身でしか対応するPasskeyを作れないところが、ちょうど認証技術のパスキーと似ているのが面白いです。
template <typename T>
class Passkey {
friend T;
explicit Passkey() = default; // TしかPasskey<T>を作れない
// Note: C++20より前ではexplicit指定しないと、
// 集成体初期化の仕様により他のクラスがPasskeyを作成できてしまう
};
class G; // 前方宣言
class F {
int data_;
int data_really_private_; // こっちはGでも見れない
public:
int get_data(Passkey<G>) const { // GのPasskeyを要求する
return data_;
}
};
class G {
int data_;
public:
int get_f_data(const F & f) const {
// 自身のPasskey<G>は作成できる
return f.get_data(Passkey<G>{});
}
};
class H {
public:
int get_f_data(const F & f) const {
// G以外は、Passkey<G>を作れない
return f.get_data(Passkey<G>{}); // コンパイルエラー
}
};
C++20以降は、集成体初期化の仕様が変更されたことから、explicit指定がなくてもよくなりました。また、Passkeyを作成するクラスが特定されているのであれば、特にテンプレートにする必要は必ずしもないです。
class G;
// Passkeyを使うクラスが決まっているならテンプレートである必要はない
class GPasskey {
friend G;
GPasskey() = default; // C++20以降はexplicit指定は不要
};
class F {
int data_;
int data_really_private_;
public:
int get_data(GPasskey) const {
return data_;
}
};
class G {
int data_;
public:
int get_f_data(const F & f) const {
return f.get_data({}); // explicitでないので{}だけでOK
}
};
Discussion