循环中的模板元图

Template Metaprogramming in loop?

本文关键字:循环      更新时间:2023-10-16

几分钟前,我是预言三词算法问题。下面的代码(算法问题的具体逻辑并不重要,因此我们需要知道的只是主要功能上方的代码只是TMP):

#include <array>
#include <algorithm>
#include <iterator>
#include <iostream>
constexpr int digit_in_ones[10] = { 6, 2, 5, 5, 4, 5, 6, 3, 7, 6 };
constexpr int createOneD(int index);
template<int ...>
struct seq
{
};
template<int A, int ...B>
struct gens : gens<A - 1, A - 1, B...>
{
};
template<int ...S>
struct gens<0, S ...>
{
    typedef seq<S...> type;
};
template<int N>
class oneDArrayMaker
{
private:
    typedef typename gens<N>::type sequence;
    template<int ...S>
    static constexpr std::array<int, N> make(seq<S ...>)
    {
        return std::array<int, N>{ {createOneD(S)...}};
    }
public:
    static constexpr std::array<int, N> oneDArr = make(sequence());
};
template<int N>
constexpr std::array<int, N> oneDArrayMaker<N>::oneDArr;
constexpr int createOneD(int index)
{
    return index < 10 ? 
        digit_in_ones[index] : 
        digit_in_ones[(index % 100) / 10] + digit_in_ones[index % 10] + 
        (index >= 100 ? digit_in_ones[index / 100] : 0);
}
int main()
{
    int n{}, ans{};
    scanf("%d", &n);
    for (int i = 0; i < 800; i++)
    {
        for (int j = 0; j < 800; j++)
        {
            auto temp = oneDArrayMaker<800>::oneDArr[i] + oneDArrayMaker<800>::oneDArr[j] + (i+j < 800 ? oneDArrayMaker<800>::oneDArr[i+j] : 100) + 4;
            if (temp == n)
            {
                ans++;
            }
        }
    }
    printf("%d", ans);
}

我知道loopif(排除constexpr functionif constexpr)是运行时,而不是编译时间。因此,诸如template specialization之类的技巧是ifloop的变电站。我从本文中学到了关于模板编程中if的愚蠢使用的教训 - 使用C 11编译时间循环 - 创建一个广义static_for实现,此处代码:

#include &lt;iostream>
template&lt;int index> void do_stuff()
{
    std::cout &lt;&lt; index &lt;&lt; std::endl;
}
template&lt;int max_index, int index = 0> void stuff_helper()
{
    if (index &lt;= max_index)
    {
        do_stuff&lt;index>();
        stuff_helper&lt;max_index, index + 1>();
    }
}
int main()
{
    stuff_helper&lt;100>();
    return 0;
}

作者的解释:

在表面上,IF语句看起来可能是终止递归的原因,例如如何使用"正常"运行时的递归算法。但这就是问题。运行时有效的工作在编译时不起作用。

这是一个无限的循环,仅停止,因为编译器将自己限制在一定的递归深度上。在Clang中,我会遇到错误致命错误:递归模板实例化超过256的最大深度。

糟糕... ,我只是说我知道的...

最后,这是我的问题:

现在,模板的实例化(特别是两张)正在编译时间。因此,所有模板在浇头代码中的实例化都应在编译时:

    for (int i = 0; i < 800; i++)
    {
        for (int j = 0; j < 800; j++)
        {
            auto temp = oneDArrayMaker<800>::oneDArr[i] + ... // 800 * 800 instantiations should be deternimated at compile time
            ...
        }
        ...
    }

我们知道1.这里的两个for loop是运行时ah,尽管它不超出模板功能/类的定义,并且仅在主函数中。2.每个auto temp = oneDArrayMaker<800>::oneDArr[i] + ...都应在编译时初始化,因此应在编译时降低800 * 800实例。

Q1:主要功能中的运行时循环与799*799编译时间模板初始化?

我的假设:在编译时,编译器知道循环的深度,因此只需展开循环,在运行时没有循环。但是我坚持认为,两个循环(I和J)在运行时也无法降低,我将主要功能更改为:

int main()
{
    int n{}, ans{}, i{}, j{};
    scanf("%d", &n);
    scanf("%d %d", &i, &j);
    std::cout << n << " " << i << " " << j << std::endl;
    for (; i < 800; i++)
    {
        for (; j < 800; j++)
        {
            auto temp = oneDArrayMaker<800>::oneDArr[i] + oneDArrayMaker<800>::oneDArr[j] + (i+j < 800 ? oneDArrayMaker<800>::oneDArr[i+j] : 100) + 4;
            if (temp == n)
            {
                ans++;
            }
        }
    }
    printf("%d", ans);
}

现在,由于scanf,必须在运行时降低ij。我只是将额外的两个0传递给stdin。

这是Alter Main功能之后的实时示例,输出为12(正确的答案是128)

它成功地编译了,没有生成警告。让我感到困惑的是,输出与原始代码不同(实时代码,其输出为128(等于Rigth答案)。

在Dubug之后,我发现关键是在更改代码之后,for (; i < 800; i++)仅一次启用i = 0,而它应该剥离1~799,这是12的原因,而不是128

Q2:如果在运行时无法降低循环的深度,并且TMP代码生存在循环中,将会发生什么?

Q3:如何解释输出12

更新:

Q3已由@Scott Brown解决,我很粗心。

Q1和Q2仍然使我感到困惑

您忘了在' for (; j < 800; j++)'之前重置J。

int main()
{
    int n{}, ans{}, i{}, j{};
    scanf("%d", &n);
    scanf("%d %d", &i, &j);
    std::cout << n << " " << i << " " << j << std::endl;
    int j_orig = j;// here
    for (; i < 800; i++)
    {
        j = j_orig;// and here
        for (; j < 800; j++)
        {
            auto temp = oneDArrayMaker<800>::oneDArr[i] + oneDArrayMaker<800>::oneDArr[j] + (i+j < 800 ? oneDArrayMaker<800>::oneDArr[i+j] : 100) + 4;
            if (temp == n)
            {
                ans++;
            }
        }
    }
    printf("%d", ans);
}