编译时反向整数序列

时间:2019-06-19 14:40:02

标签: c++

我一直在阅读整数序列,但对下面的程序是如何工作的却很难理解。当我解决这个问题时,我根本不会得到相反的顺序。外面有人可以向我详细解释该程序吗?为我的考试准备而绞尽脑汁。谢谢!

#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>());
}

2 个答案:

答案 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 ++ 语法,而仅仅是旨在显示递归过程的“图形表示​​”。