如何存储可变参数模板?

是否有可能以某种方式存储参数包供以后使用?

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; 

而且,你必须稍微改变你的构造函数。 特别是,用std::make_tuple初始化args ,并在参数列表中允许通用引用:

 template <typename F, typename... Args> Action(F&& func, Args&&... args) : f(std::forward<F>(func)), args(std::forward<Args>(args)...) {} 

而且,你将不得不像这样build立一个序列发生器:

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

而且它! 所以现在你的class级应该是这样的:

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

这是您在Coliru上的完整程序。


更新:这是一个帮助器方法,通过该方法模板参数的规范是不必要的:

 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是一个函数,你需要将其作为数据成员来存储调用它的结果。 该结果的数据types不容易预测(标准甚至没有精确地指定),所以我使用decltypestd::declval的组合在编译时计算该数据types。 请参阅上面的Action::bind_type的定义。

另外请注意我在模板构造函数中如何使用通用引用。 这确保您可以传递与类模板参数T...完全不匹配的参数(例如,您可以对某些T使用右值引用,并将它们按原样转发给bind调用)。

最后一点:如果你想把参数作为引用存储(所以你传递的函数可以修改,而不是仅仅使用它们),你需要使用std::ref把它们包装在引用对象中。 只是通过一个T &会创造一个价值的副本,而不是一个参考。

Coliru上的操作代码

我认为你有一个XY问题。 为什么要在所有的麻烦存储参数包,当你可以在调用网站使用lambda? 即

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