以下来自用户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:这种技术可以归类为模板元编程吗?
答案 0 :(得分:13)
让我们来看看这里发生了什么:
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
为&#34; 3&#34;和...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
为&#34; 2&#34;。现在,让我们进行下一步,看看如何定义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>
会发生什么?那么,在专业化中,&#34; S ...&#34;变成&#34; 0,1,2和#34;所以这就变成了一种说法:
struct gens<0, 0, 1, 2> {
typedef seq<0, 1, 2> type;
}
现在,请记住,所有这些都是公开继承的,&#34; elephant-style&#34;,所以:
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...
部分变为&#34; 0,1,2&#34;,所以
std::get<S>(params)...
成为扩展为的参数包:
std::get<0>(params), std::get<1>(params), std::get<2>(params),
这就是元组如何成为参数包。
答案 1 :(得分:1)
在C ++ 17中,您可以使用“ if constexpr”来创建序列包装器:
template <int indxMax, template <int... > class spack, int ... seq>
constexpr auto get_seq17()
{
static_assert(indxMax >= 0, "Sequence size must be equal to or greater than 0!");
if constexpr (indxMax > 0)
{
typedef decltype(spack<indxMax, seq...>{}) frst;
constexpr int next = indxMax - 1;
return get_seq17<next, spack, indxMax, seq...>();
}
else
{
return spack<indxMax, seq...>{};
}
}
template <int indxMax, template <int...> class pack>
struct seq_pack
{
typedef decltype(get_seq17<indxMax, pack>()) seq;
};
//creating a sequence wrapper
template <int ... seq>
struct seqpack {};
//usage
seq_pack<4, seqpack>::seq; //seqpack<0, 1, 2, 3, 4>
尽管此实现更容易理解,但最好使用Julius,如{{3}}在下面的评论中所述。