【问题标题】:Compile time recursion and conditionals编译时间递归和条件
【发布时间】:2012-02-04 07:15:55
【问题描述】:

我正在阅读对"Printing 1 to 1000 without loop or conditionals" 的回复,我想知道为什么有必要在最佳答案中包含 NumberGeneration 的特殊情况。

如果我删除它并在模板中添加对 N == 1 的检查(下面的代码),代码编译失败,并显示“模板实例化深度超过最大值”,但我不知道为什么。条件在编译时的处理方式不同吗?

#include <iostream>

template<int N>
struct NumberGeneration
{
    static void out(std::ostream& os)
    {
        if (N == 1)
        {
            os << 1 << std::endl;
        }
        else
        {
            NumberGeneration<N-1>::out(os);
            os << N << std::endl;
        }
    }
};

int main()
{
    NumberGeneration<1000>::out(std::cout);
}

【问题讨论】:

    标签: c++ compile-time


    【解决方案1】:

    我很确定这是特定于编译器的;一些编译器可能会尝试生成if/else 的两个分支,无论N 的值是多少,在这种情况下编译无论如何都会失败。其他编译器可能会在编译时评估条件,并且只为执行的分支生成代码,在这种情况下编译会成功。

    更新:或者正如 Luc 在 cmets 中所说,可能是编译器必须生成两个分支,这样代码总是会失败。我不太确定是哪种情况,但无论哪种方式,依靠运行时条件来控制编译时代码生成都是一个坏主意。

    最好使用专业化:

    template <int N>
    struct NumberGeneration
    {
        static void out(std::ostream & os)
        {
            NumberGeneration<N-1>::out(os);
            os << N << std::endl;
        }
    };
    
    template <>
    void NumberGeneration<1>::out(std::ostream & os)
    {
        os << 1 << std::endl;
    }
    

    (或者您可以通过专门针对 N=0 来稍微缩短此时间,使用不执行任何操作的 out 函数)。

    另外,请注意某些编译器可能不支持深度递归模板; C++03 建议的最小支持深度仅为 17,C++11 增加到 1024。您应该检查编译器的限制是多少。

    【讨论】:

    • 嗯,我认为编译器必须实例化模板,即使它可以检测到代码在运行时永远不会到达,所以我认为这段代码永远不可能完成编译,无论你使用什么编译器。
    • 你不需要专门化整个结构,你可以只为函数做。
    • @PaulManta:确实可以。谢谢。
    【解决方案2】:

    这是正确的做法:

    template<int N>
    struct NumberGeneration
    {
        static void out(std::ostream& os);
    };
    
    template<int N>
    void NumberGeneration<N>::out(std::ostream& os)
    {
        NumberGeneration<N-1>::out(os);
        os << N << std::endl;
    }
    
    template<>
    void NumberGeneration<1>::out(std::ostream& os)
    {
        os << 1 << std::endl;
    }
    
    int main()
    {
        NumberGeneration<20>::out(std::cout);
    }
    

    这称为模板专业化:您,程序员,为模板的特定实例化提供替代定义。您可以专门化整个模板,也可以只专门化其中的一部分,就像我在这里所做的那样(我只专门化了函数,而不是整个结构)。

    【讨论】:

      【解决方案3】:

      模板在编译时实例化,模板特例防止编译器在编译时递归低于1。

      if 子句是在运行时评估的,因此编译器在编译您的代码时已经失败了。

      【讨论】:

        【解决方案4】:

        一般来说,代码中的条件N == 1 是在运行时评估的(尽管编译器可能会优化掉它),而不是在编译时。因此,else 子句中的模板实例化递归永远不会终止。另一方面,NumberGeneration&lt;1&gt; 在编译时进行评估,因此充当此递归模板的终止情况。

        【讨论】:

          【解决方案5】:

          条件if 不会在编译时处理。它将在运行时处理。

          因此,即使 N=1,编译器也会不断生成 NumberGenerator,然后生成 NumberGenerator ...,直到达到模板实例化深度。

          【讨论】:

            【解决方案6】:

            代码生成和编译不会根据条件分支!考虑一下:

            // don't declare bar()!
            
            void foo()
            {
                 if (false) { bar(); }
            }
            

            如果你从未声明bar(),这是一个编译错误,即使内部范围永远无法到达。出于同样的原因,NumberGeneration&lt;N-1&gt; 总是被实例化,无论是否可以到达那个分支,并且你有无限递归。

            确实,条件句的静态类似物正是模板特化:

            template <> struct NumberGeneration<0> { /* no more recursion here */ };
            

            【讨论】:

            • 谢谢,这是有道理的,我并没有真正考虑编译时实例化,而是希望它只在到达代码时才完成。
            • @baris.m:但是你所说的“代码已到达”有一个关键的微妙之处:编译器在编译期间到达一次,总是,然后再次有条件地 i> 在程序执行期间的运行时。
            • 在我的评论中,我谈论的是在运行时被联系到。你的回答很有道理。
            【解决方案7】:

            我想知道为什么有必要为 NumberGeneration 在最佳答案中。

            因为这是递归的结束条件!没有那个递归怎么会结束?

            【讨论】:

            • 我添加到模板中的 if N == 1 条件呢?在这种情况下,我们不会用 N-1 实例化一个新的,所以我希望递归停止。
            • @baris.m :您不应该删除条件 1 的模板专业化,因为这是编译结束计算/编译的结束条件。
            • @baris.m :您添加的条件没有帮助,因为编译不会关心它并在编译期间结束编译。您添加的检查仅在运行时有用,而不是编译时。
            • @baris.m:如果您仍有任何疑问,请阅读此内容:en.wikipedia.org/wiki/Template_metaprogramming
            【解决方案8】:

            这是因为整数可以是负数,并且运行时代码(if 检查)不会阻止编译器使用 0、-1、-2 等实例化模板。编译器可能能够摆脱您的建议,但是如果实例化其他模板(0,-1,...)具有您所依赖的副作用怎么办?在这种情况下,编译器不能不为您实例化它们。

            简而言之,就像 all 递归一样,您必须提供自己的基本情况。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2023-03-30
              • 1970-01-01
              • 2011-07-13
              • 1970-01-01
              • 2010-12-19
              • 1970-01-01
              • 2014-04-28
              相关资源
              最近更新 更多