以下来自用户Faheem Mitha的代码基于用户Johannes Schaub-litb在此SO中的回答。这段代码完美地实现了我所追求的,即将tuple
转换为参数包,但是我对这段代码的理解不够充分,因此我想我将进行一个新的讨论,该模板可能有助于对像我这样的新手进行元编程。因此,请原谅重复的过帐。
现在进入代码
#include <tuple>
#include <iostream>
using std::cout;
using std::endl;
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { };
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
double foo(int x, float y, double z)
{
return x + y + z;
}
template <typename ...Args>
struct save_it_for_later
{
std::tuple<Args...> params;
double(*func)(Args...);
double delayed_dispatch()
{
return callFunc(typename gens<sizeof...(Args)>::type()); // Item #1
}
template<int ...S>
double callFunc(seq<S...>)
{
return func(std::get<S>(params) ...);
}
};
int main(void)
{
std::tuple<int, float, double> t = std::make_tuple(1, (float)1.2, 5);
save_it_for_later<int, float, double> saved = { t, foo };
cout << saved.delayed_dispatch() << endl;
return 0;
}
我完全被上面的#1所困扰:
typename
在该行上有什么作用? gens<sizeof...(Args)>::type()
将扩展为gens<3>::type()
,但这似乎既不匹配template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { };
也不匹配template<int ...S> struct gens<0, S...>
。我显然不明白要点,如果有人可以解释这里发生的事情,我将很高兴。 我确实知道
callFunc
会以callFunc(seq<0,1,2>)
的形式被调用,并且此方法的return语句本身会扩展为return func(std::get<0>(params), std::get<1>(params), std::get<2>(params)
,这就是使该方案起作用的原因,但是我无法确定如何生成此seq<0,1,2>
类型。注意:不能使用
std::index_sequence_for
,我的编译器不支持C++ 14功能。PS:这项技术可以归类为模板元编程吗?
最佳答案
让我们看看这里发生了什么:
template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { };
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
第一个是通用模板,第二个是当第一个模板参数为0时应用的特化。
现在,拿一支纸和铅笔,写下如何
gens<3>
由上述模板定义。如果您的答案是:
struct gens<3> : public gens<2, 2>
那你是对的。这就是
N
为“3”且...S
为空时第一个模板的扩展方式。因此,gens<N - 1, N - 1, S...>
变为gens<2, 2>
。现在,让我们继续前进,看看如何定义
gens<2, 2>
: struct gens<2, 2> : public gens<1, 1, 2>
在此,在模板扩展中,
N
为2,而...S
为“2”。现在,让我们进行下一步,看看如何定义gens<1, 1, 2>
: struct gens<1, 1, 2> : public gens<0, 0, 1, 2>
好的,现在如何定义
gens<0, 0, 1, 2>
?现在可以通过特化来定义: template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
那么,这里的
struct gens<0, 0, 1, 2>
会发生什么?好吧,在特化中,“S ...”变成“0,1,2”,因此从某种意义上说变成: struct gens<0, 0, 1, 2> {
typedef seq<0, 1, 2> type;
}
现在,请记住,所有这些都是“大象风格”彼此公开继承的,因此:
gens<3>::type
最终成为typedef声明
struct seq<0, 1, 2>
然后,以下代码使用另一个模板将其用于将元组转换为参数包:
double delayed_dispatch()
{
return callFunc(typename gens<sizeof...(Args)>::type()); // Item #1
}
...Args
是元组参数。因此,如果该元组中包含三个元素,则sizeof(...Args)
为3,正如我在上文中所解释的,gens<sizeof...(Args)>::type()
变为gens<3>::type()
,又称为seq<0, 1, 2>()
。因此,现在:
template<int ...S>
double callFunc(seq<S...>)
{
return func(std::get<S>(params) ...);
}
S...
部分变为“0,1,2”,因此std::get<S>(params)...
成为参数包,并扩展为:
std::get<0>(params), std::get<1>(params), std::get<2>(params),
这就是元组变成参数包的方式。
关于c++ - 元组到参数包,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36612596/