是否可以以某种方式存储参数包供以后使用?
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();
}
答案 0 :(得分:55)
要完成您想要完成的任务,您必须将模板参数存储在元组中:
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);
}
};
Here is your full program on 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();
}
答案 1 :(得分:18)
您可以使用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
使用rvalue引用,并且您将按原样将它们转发到{{} 1}}打电话。)
最后注意事项:如果要将参数存储为引用(以便传递的函数可以修改而不是仅仅使用它们),则需要使用bind
将它们包装在引用对象中。仅传递std::ref
将创建值的副本,而不是参考。
答案 2 :(得分:6)
这个问题来自C ++ 11天。但是对于那些现在在搜索结果中找到它的人,请进行一些更新:
std::tuple
成员仍然是通常存储参数的直接方法。 (如果您只想调用一个特定的函数,但类似于@jogojapan's的std::bind
解决方案也可以使用,但是如果您想通过其他方式访问参数,或者将参数传递给多个函数,则该解决方案也无法使用等)
在C ++ 14和更高版本中,std::make_index_sequence<N>
or std::index_sequence_for<Pack...>
可以代替0x499602D2's solution中出现的helper::gen_seq<N>
工具:
#include <utility>
template <typename... Ts>
class Action
{
// ...
template <typename... Args, std::size_t... Is>
void func(std::tuple<Args...>& tup, std::index_sequence<Is...>)
{
f(std::get<Is>(tup)...);
}
template <typename... Args>
void func(std::tuple<Args...>& tup)
{
func(tup, std::index_sequence_for<Args...>{});
}
// ...
};
在C ++ 17和更高版本中,std::apply
可用于解开元组的包装:
template <typename... Ts>
class Action
{
// ...
void act() {
std::apply(f, args);
}
};
这里的a full C++17 program显示了简化的实现。我还更新了make_action
来避免使用tuple
中的引用类型,这对于右值参数总是不利的,而对左值参数而言却是相当危险的。
答案 3 :(得分:2)
我认为你有一个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();
}