C++ 如何存储可变参数模板参数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16868129/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to store variadic template arguments?
提问by Eric B
Is it possible to store a parameter pack somehow for a later use?
是否可以以某种方式存储参数包以备后用?
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
}
}
Then later on:
然后后来:
void main(){
Action<int,int> add([](int x, int y){std::cout << (x+y);}, 3, 4);
//...
add.act();
}
采纳答案by 0x499602D2
To accomplish what you want done here, you'll have to store your template arguments in a tuple:
要在此处完成您想要完成的操作,您必须将模板参数存储在一个元组中:
std::tuple<Ts...> args;
Furthermore, you'll have to change up your constructor a bit. In particular, initializing args
with an std::make_tuple
and also allowing universal references in your parameter list:
此外,您必须稍微更改构造函数。特别是,args
使用std::make_tuple
和初始化还允许参数列表中的通用引用:
template <typename F, typename... Args>
Action(F&& func, Args&&... args)
: f(std::forward<F>(func)),
args(std::forward<Args>(args)...)
{}
Moreover, you would have to set up a sequence generator much like this:
此外,您必须像这样设置一个序列生成器:
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...> {};
}
And you can implement your method in terms of one taking such a generator:
您可以使用这样的生成器来实现您的方法:
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);
}
And that it! So now your class should look like this:
和它!所以现在你的类应该是这样的:
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.
Update: Here is a helper method by which specification of the template arguments aren't necessary:
更新:这是一个辅助方法,不需要指定模板参数:
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();
}
回答by jogojapan
You can use std::bind(f,args...)
for this. It will generate a movable and possibly copyable object that stores a copy of the function object and of each of the arguments for later use:
您可以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;
}
Notice that std::bind
is a function and you need to store, as data member, the result of calling it. The data type of that result is not easy to predict (the Standard does not even specify it precisely), so I use a combination of decltype
and std::declval
to compute that data type at compile time. See the definition of Action::bind_type
above.
请注意,这std::bind
是一个函数,您需要将调用它的结果存储为数据成员。这一结果的数据类型是不容易预测(标准甚至没有精确地指定它),所以我用的组合decltype
,并std::declval
计算在编译时该数据类型。见Action::bind_type
上面的定义。
Also notice how I used universal references in the templated constructor. This ensures that you can pass arguments that do not match the class template parameters T...
exactly (e.g. you can use rvalue references to some of the T
and you will get them forwarded as-is to the bind
call.)
还要注意我如何在模板化构造函数中使用通用引用。这确保您可以传递与类模板参数不完全匹配的参数T...
(例如,您可以使用某些 rvalue 引用,T
并且您将按原样将它们转发到bind
调用。)
Final note: If you want to store arguments as references (so that the function you pass can modify, rather than merely use, them), you need to use std::ref
to wrap them in reference objects. Merely passing a T &
will create a copy of the value, not a reference.
最后说明:如果您想将参数存储为引用(以便您传递的函数可以修改,而不仅仅是使用它们),您需要使用std::ref
将它们包装在引用对象中。仅传递 aT &
将创建值的副本,而不是引用。
回答by Casey
I think you have an XY problem. Why go to all the trouble to store the parameter pack when you could just use a lambda at the callsite? i.e.,
我认为你有一个 XY 问题。当您可以在调用站点使用 lambda 时,为什么还要费力地存储参数包?IE,
#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();
}
回答by aschepler
This question was from C++11 days. But for those finding it in search results now, some updates:
这个问题来自 C++11 天。但是对于那些现在在搜索结果中找到它的人,一些更新:
A std::tuple
member is still the straightforward way to store arguments generally. (A std::bind
solution similar to @jogojapan'swill also work if you just want to call a specific function, but not if you want to access the arguments in other ways, or pass the arguments to more than one function, etc.)
通常,std::tuple
成员仍然是存储参数的直接方式。(如果您只想调用特定函数,则std::bind
类似于@jogojapan 的解决方案也适用,但如果您想以其他方式访问参数,或将参数传递给多个函数等,则不适用。)
In C++14 and later, std::make_index_sequence<N>
or std::index_sequence_for<Pack...>
can replace the helper::gen_seq<N>
tool seen in 0x499602D2's solution:
在 C++14 及更高版本中,std::make_index_sequence<N>
或者std::index_sequence_for<Pack...>
可以替换helper::gen_seq<N>
在0x499602D2 的解决方案中看到的工具:
#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...>{});
}
// ...
};
In C++17 and later, std::apply
can be used to take care of unpacking the tuple:
在 C++17 及更高版本中,std::apply
可用于处理元组的解包:
template <typename... Ts>
class Action
{
// ...
void act() {
std::apply(f, args);
}
};
Here's a full C++17 programshowing the simplified implementation. I also updated make_action
to avoid reference types in the tuple
, which was always bad for rvalue arguments and fairly risky for lvalue arguments.
这是一个完整的 C++17 程序,显示了简化的实现。我还更新make_action
以避免 中的引用类型tuple
,这对于右值参数总是不利的,而对于左值参数则相当危险。