何の話か
C++ 初心者です.
実際にそういうことをしたい場面があるわけではないんだけど,何故か以下のようなことをふと考えた.だけ.
class X を Y の friend にすると,X から Y の全てを触れる.
→ この「全て」ってのが嫌な場合にはどうする? っていう.
例えばこんな↓場合,どうすればいいかな? と.
class Y
{
friend class X;
private:
//コレはXに触らせたいが…
void PrivateFunc1(){ std::cout << "PF1\n"; }
//以下はXにも触らせたくない
void PrivateFunc2(){ std::cout << "PF2\n"; }
int m_Val = 77;
};
(1)Yを2分割
そんなことのために継承とか,何か嫌だな.
//Xがfirendな部分だけのclassを作って…
class IF_for_X
{
friend class X;
protected:
void PrivateFunc1(){ std::cout << "PF1\n"; }
};
//↑を継承する
class Y : public IF_for_X
{
private:
void PrivateFunc2(){ std::cout << "PF2\n"; }
int m_Val = 77;
};
(2)三店方式
直に friend にするのではなく,X と Y の間に1つそれ用のを噛ませる.
class Y
{
public:
class IF_for_X
{
friend class X;
static void PrivateFunc1( Y &y ){ y.PrivateFunc1(); }
};
private:
void PrivateFunc1(){ std::cout << "PF1\n"; }
void PrivateFunc2(){ std::cout << "PF2\n"; }
int m_Val = 77;
};
class X
{
public:
void Test( Y &y ){ Y::IF_for_X::PrivateFunc1( y ); }
};
MEMO :
Y::IF_for_X 型は最初はこんなの↓を考えたんだけど,コレ別にこの型のオブジェクトとか要らねぇよな……ということで↑の形に.
class IF_for_X
{//Xはコレを作れる
friend class X;
IF_for_X(){}
public:
void PrivateFunc1( Y &y ){ y.PrivateFunc1(); }
};
(3)Xにしか作れないものを要求
public だけども実質 X からしか使えないでしょ,っていう.
(→ これは "Passkey Idiom" というかっこいい名前が付いた方法である旨のコメントいただきました.)
class Permit
{//Xにしか作れない
friend class X;
Permit(){}
};
class Y
{
public:
//Xにしか作れないものを引数にした public なメンバ関数を用意する
void PF1( const Permit & ){ PrivateFunc1(); }
private:
void PrivateFunc1(){ std::cout << "PF1\n"; }
void PrivateFunc2(){ std::cout << "PF2\n"; }
int m_Val = 77;
};
class X
{
public:
void Test( Y &y ){ y.PF1( Permit{} ); }
void Test2( Y &y ){ y.PF1( {} ); } //{}とだけ書くのでもいける模様
};
……とかいうのを実験してたところ,↑の Permit のコンストラクタを
Permit() = default;
って書いたら,
int main()
{
Permit p; //エラー:アクセスできません
Permit *p = new Permit(); //エラー:アクセスできません
Permit p{}; // Permit の ctor を =default とすると通ってしまう?
}
という結果になって困惑中.Why?
追記
Why?
に関してコメントいただいた.
Permit() = default; とした場合,この Permit 型が「集成体」となる要件に合致してしまい, Permit p{}; が集成体初期化だということになってしまうという話だ.
どうやら explicit Permit() = default; とすれば,「集成体」となる要件から外れるようだが,そうすると
void Test2( Y &y ){ y.PF1( {} ); } //{}とだけ書くのでもいける模様
のところがいけなくなってしまうし,そうでなくとも3日後には「この explicit は何でついてるの? 意味わかんないんですけど?」とかなるであろうことは明白であろうから,ここは素直に(?) Permit(){} としておくべきであろう.
追記2
こんな悪さをすることはできなくもないかも.
class X
{
public: //オラァ! みなさんどうぞお使いください!
static Permit CreatePermit(){ return {}; }
};
……とか思ったけど,そりゃあどんな形だろうが,X が自身を経由地にするような実装をすれば全てがぶち壊しになるのは同じだから考えるだけ無駄だな.
(4)ググった → (1)と似てるけど違う形みたいな
もう自分では思いつかないのでググったら,以下のようなのを見つけた.
(正確な出所を忘れちゃったけど, Stack Overflow だったような記憶)
//protected と private で書き分けたのを用意して……
class Y_
{
protected: //Xに使わせたいやつは protected
void PrivateFunc1(){ std::cout << "PF1\n"; }
private: //そうでないやつは private
void PrivateFunc2(){ std::cout << "PF2\n"; }
int m_Val = 77;
};
//↑を継承する
class Y : public Y_ { friend class X; };
class X
{
public:
void Test( Y &y ){ y.PrivateFunc1(); } //こっちは OK
void Test2( Y &y ){ y.PrivateFunc2(); } //こっちは Error
};
どうでもいい話
ところで, friend って書く場合というのは,
【俺が friend class X; とか書くならば,俺が(?)その class X も用意するよね】
ということになるんじゃないかと思うんだけど,
別に class X が無くともコンパイルエラーになったりはしないんだよねコレ.
つまり,
【俺は class Y に friend class X; とだけ書いておくぜ!】
っていうコードを書いても良い(←文法的な意味で)ということだ.
そのコードの意味するところとは……
「この class Y を好き勝手にしたくなった場合には X という名前の class を作ってどうぞ」というメッセージ?