enabled_shared_from_thisを用いる場合、コンストラクタをprivateにしてshared_ptrを返すcreate関数を用意する方法が有効です。
だけど、create関数内でmake_sharedを使いたいけどprivateなコンストラクタが呼べないという問題があります。
その問題を回避するため、関数内クラスと継承を用いた手法があります。
std::make_shared から private コンストラクタを呼び出す
#include <memory>
struct X : std::enable_shared_from_this<X>
{
std::shared_ptr<X> create();
private:
X(){}
};
std::shared_ptr<X>
X::create()
{
struct Helper:X { Helper():X(){} };
return std::make_shared<Helper>();
// return std::make_shared<X>(); Error! : ホントはこう書きたい
}
しかし、この手法だとprivateコンストラクタで引数を多くとるときに不便です。
#include <memory>
struct X : std::enable_shared_from_this<X>
{
std::shared_ptr<X> create(int a,int b,int c);
private:
X(int a_,int b_,int c_):a(a_),b(b_),c(c_){}
int a,b,c;
};
std::shared_ptr<X>
X::create(int a,int b,int c)
{
struct Helper:X { Helper(int a,int b,int c):X(a,b,c){} }; // ヘルパークラスに引数をたくさん書かないといけない!
return std::make_shared<Helper>(a,b,c);
}
そこで、クラス内テンプレートクラスとして汎用のCreateHelperクラスを用意して、
ヘルパークラスの引数記載を省略するようにします。
#include <memory>
struct X : std::enable_shared_from_this<X>
{
std::shared_ptr<X> create(int a,int b,int c);
private:
X(int a_,int b_,int c_):a(a_),b(b_),c(c_){}
int a,b,c;
// クラス内テンプレートクラスを用いたヘルパークラス
template<typename Base>
struct CreateHelper:Base
{
template<typename... Args>
explicit CreateHelper(Args&&... args):Base(std::forward<Args>(args)...){}
};
};
std::shared_ptr<X>
X::create(int a,int b,int c)
{
return std::make_shared<CreateHelper<X> >(a,b,c);
}
今回のCreateHelperはマクロにしてあげれば使いまわせるのでオススメです。
#include <memory>
#define CREATE_HELPER \
template<typename Base> \
struct CreateHelper:Base \
{ \
template<typename... Args> \
explicit CreateHelper(Args&&... args):Base(std::forward<Args>(args)...){} \
}
struct X : std::enable_shared_from_this<X>
{
std::shared_ptr<X> create(int a,int b,int c);
private:
X(int a_,int b_,int c_):a(a_),b(b_),c(c_){}
int a,b,c;
CREATE_HELPER;
};
std::shared_ptr<X>
X::create(int a,int b,int c)
{
return std::make_shared<CreateHelper<X> >(a,b,c);
}