C++

クラス内テンプレートクラスと継承を利用してmake_sharedでprivateコンストラクタを呼ぶ

More than 1 year has passed since last update.

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);
}