パラメータパックを後で使用するために何らかの方法で保存することは可能ですか?
template <typename... T>
class Action {
private:
std::function<void(T...)> f;
T... args; // <--- something like this
public:
Action(std::function<void(T...)> f, T... args) : f(f), args(args) {}
void act(){
f(args); // <--- such that this will be possible
}
}
その後:
void main(){
Action<int,int> add([](int x, int y){std::cout << (x+y);}, 3, 4);
//...
add.act();
}
ここでやりたいことを達成するには、テンプレート引数をタプルに保存する必要があります。
std::Tuple<Ts...> args;
さらに、コンストラクタを少し変更する必要があります。特に、args
をstd::make_Tuple
で初期化し、パラメーターリストでユニバーサル参照を許可します。
template <typename F, typename... Args>
Action(F&& func, Args&&... args)
: f(std::forward<F>(func)),
args(std::forward<Args>(args)...)
{}
さらに、次のようなシーケンスジェネレーターをセットアップする必要があります。
namespace helper
{
template <int... Is>
struct index {};
template <int N, int... Is>
struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};
template <int... Is>
struct gen_seq<0, Is...> : index<Is...> {};
}
そして、あなたはそのようなジェネレーターを取るものに関してあなたのメソッドを実装することができます:
template <typename... Args, int... Is>
void func(std::Tuple<Args...>& tup, helper::index<Is...>)
{
f(std::get<Is>(tup)...);
}
template <typename... Args>
void func(std::Tuple<Args...>& tup)
{
func(tup, helper::gen_seq<sizeof...(Args)>{});
}
void act()
{
func(args);
}
そしてそれ!したがって、クラスは次のようになります。
template <typename... Ts>
class Action
{
private:
std::function<void (Ts...)> f;
std::Tuple<Ts...> args;
public:
template <typename F, typename... Args>
Action(F&& func, Args&&... args)
: f(std::forward<F>(func)),
args(std::forward<Args>(args)...)
{}
template <typename... Args, int... Is>
void func(std::Tuple<Args...>& tup, helper::index<Is...>)
{
f(std::get<Is>(tup)...);
}
template <typename... Args>
void func(std::Tuple<Args...>& tup)
{
func(tup, helper::gen_seq<sizeof...(Args)>{});
}
void act()
{
func(args);
}
};
更新:これは、テンプレート引数の指定が不要なヘルパーメソッドです。
template <typename F, typename... Args>
Action<Args...> make_action(F&& f, Args&&... args)
{
return Action<Args...>(std::forward<F>(f), std::forward<Args>(args)...);
}
int main()
{
auto add = make_action([] (int a, int b) { std::cout << a + b; }, 2, 3);
add.act();
}
これには std::bind(f,args...)
を使用できます。後で使用するために、関数オブジェクトと各引数のコピーを保存する、移動可能なコピー可能なオブジェクトを生成します。
#include <iostream>
#include <utility>
#include <functional>
template <typename... T>
class Action {
public:
using bind_type = decltype(std::bind(std::declval<std::function<void(T...)>>(),std::declval<T>()...));
template <typename... ConstrT>
Action(std::function<void(T...)> f, ConstrT&&... args)
: bind_(f,std::forward<ConstrT>(args)...)
{ }
void act()
{ bind_(); }
private:
bind_type bind_;
};
int main()
{
Action<int,int> add([](int x, int y)
{ std::cout << (x+y) << std::endl; },
3, 4);
add.act();
return 0;
}
std::bind
は関数であり、呼び出しの結果をデータメンバーとして保存する必要があることに注意してください。その結果のデータ型を予測するのは簡単ではありません(標準では正確に指定することすらありません)。したがって、コンパイル時にdecltype
と std::declval
の組み合わせを使用してそのデータ型を計算します。上記のAction::bind_type
の定義を参照してください。
また、テンプレートコンストラクターでユニバーサル参照を使用した方法にも注目してください。これにより、クラステンプレートパラメーターT...
と正確に一致しない引数を渡すことができます(たとえば、T
の一部に右辺値参照を使用でき、bind
呼び出しにそのまま転送されます)。
最後の注意:引数を参照として格納する場合(渡す関数が単に引数を使用するのではなく変更できるようにするため)、std::ref
を使用して参照オブジェクトにラップする必要があります。 T &
を渡すだけで、参照ではなく値のコピーが作成されます。
XYに問題があると思います。コールサイトでラムダを使用できるのに、なぜパラメーターパックを保存するためにすべての問題に取り組むのですか?すなわち、
#include <functional>
#include <iostream>
typedef std::function<void()> Action;
void callback(int n, const char* s) {
std::cout << s << ": " << n << '\n';
}
int main() {
Action a{[]{callback(13, "foo");}};
a();
}