我一直在阅读整数序列,但对下面的程序是如何工作的却很难理解。当我解决这个问题时,我根本不会得到相反的顺序。外面有人可以向我详细解释该程序吗?为我的考试准备而绞尽脑汁。谢谢!
#include <iostream>// a sequence container -------------------------
template<int ...>
struct ints
{
};
// reverse integer sequence ---------------------
template<int C, int N, int... Is>
struct rev_seq : rev_seq<C - 1, N, N - C, Is...>
{
};
template<int N, int... Is>
struct rev_seq<0, N, Is...>
{
using type = ints<N, Is...>;
};
// convenience alias ----------------------------
template<int N>
using RS = typename rev_seq<N, N>::type;
template<int... Is>
void fU(ints<Is...>&& s)
{
for (auto i : { Is... }) std::cout << i << " ";
std::cout << std::endl;
}
int main()
{
fU(RS<5>());
}
答案 0 :(得分:6)
RS<2>()
实例化rev_seq<2, 2>::type
rev_seq<2, 2>::type
是rev_seq的主要模板(不是专门的模板):
template<int C, int N, int... Is>
struct rev_seq : rev_seq<C - 1, N, N - C, Is...>{}
这是一个递归声明,因此它是从其自身的版本派生的,如下所示:
rev_seq<2, 2, (empty int... Is pack)>
源自
rev_seq<2-1, 2, 2 - 2>
是rev_seq<1, 2, 0>
最后的0
是基类的int... Is
包的一部分
这再次发生
rev_seq<1, 2, 0>
源自
rev_seq<1-1, 2, 2-1, 0>
是rev_seq<0, 2, (1, 0)>
看看最后一个参数参数如何附加到包中?
rev_seq<0, 2, (1, 0)>
与rev_seq
的以下模板 specialization 相匹配:
template<int N, int... Is>
struct rev_seq<0, N, Is...>
{
using type = ints<N, Is...>;
};
请注意,此struct
并非源自任何东西
此时,类中的type
类型变为
ints<2, 1, 0>
看看专业化是如何使我们将N
附加到序列的前面吗?
最后,我们将构造的ints
传递给一个函数:
template<int... Is>
void fU(ints<Is...>&& s)
{
for (auto i : { Is... }) std::cout << i << " ";
std::cout << std::endl;
}
循环遍历整数
for (auto i : { Is... })
这里{ Is...}
是一个包扩展,创建了一个初始化列表供我们迭代。有趣的是,这是您可以创建和使用初始化列表的少数几个地方之一,几乎所有其他情况都被降级为匹配某个类的std :: initializer_list构造函数重载(例如std::vector
)
答案 1 :(得分:2)
假设您已经了解variadic template
背后的理论,我建议的最佳方法是“手动展开”模板定义。
大多数情况下,可变参数模板都采用递归方法。
让我们尝试做这个练习。
核心部分是:RS<5>()
。那只是rev_seq<5, 5>::type
的一个实例。好吧,让我们深入rev_seq
。
其声明:
template<int C, int N, int... Is>
struct rev_seq // ...
因此该实例将被“映射”:
template<5, 5, $>
struct rev_seq // ...
其中$
只是一个占位符,表示空的可变参数列表。
rev_seq
递归继承:
template<5, 5, $>
struct rev_seq : rev_seq <4, 5, 0, $> {}
当然rev_seq <4, 5, 0, $>
(即rev_seq<4, 5, {0}>
)是继承的,依此类推。
<5, 5, $> ->
<4, 5, {0}> ->
<3, 5, {1, 0}> ->
<2, 5, {2, 1, 0}> ->
<1, 5, {3, 2, 1, 0}> ->
<0, 5, {4, 3, 2, 1, 0}>
当第一个模板参数为0
时,我们停止。因为在这种情况下,我们有部分模板专业化。
在这里,您可以看到递归策略中与“基本案例” 的类比。
因此,我们终于得到了继承:
struct rev_seq<0, N, Is...>
{
using type = ints<N, Is...>;
};
在您的情况下:
struct rev_seq<0, 5, {4, 3, 2, 1, 0}>
{
using type = ints<5, {4, 3, 2, 1, 0}>;
};
请注意,ints
只是一个可变列表。也就是说:ints<5, {4, 3, 2, 1, 0}>
实际上是ints<{5, 4, 3, 2, 1, 0}>
。
因此,最后,您仅使用ints
的特定实例调用“打印功能”:
template<{5, 4, 3, 2, 1, 0}>
void fU(ints<{5, 4, 3, 2, 1, 0}>&& s)
{
for (auto i : { 5, 4, 3, 2, 1, 0 }) std::cout << i << " ";
std::cout << std::endl;
}
请注意,这不是有效的 C ++ 语法,而仅仅是旨在显示递归过程的“图形表示”。