【问题标题】:Recursion using template meta programming使用模板元编程进行递归
【发布时间】:2020-03-20 21:57:24
【问题描述】:

谁能给我解释一下,为什么使用模板函数的第一个调用陷入了无限循环,而第二个编译时函数运行正常?

#include <iostream>
using namespace std;

template<int N, int M>
struct commondivs {                                              
  static const int val = (N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val;
};

template<int N>
struct commondivs<N,N> {
  static const int val = N;
};


int commondiv(int N, int M){
    if(N==M){
        return N;
    }   
    return (N<M)?commondiv(N,(M-N)):commondiv((N-M),M);     
}

int main() {

    cout << commondivs<9,6>::val << endl;
    cout << commondiv(9,6) << endl;
    return 0;
}

【问题讨论】:

  • 目标是使用模板元编程。 constexpr 不是一个选项。
  • 添加了 c++98 标记以明确表明 constexpr 不是一个选项。 (它是在 C++11 中引入的)。这确实使现有答案无效。 Exxul,请说明您仅限于哪个 C++ 版本。
  • 对不起,我删除了标签。

标签: c++ templates template-meta-programming


【解决方案1】:
(N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val

此行会导致 commondivs&lt;N,(M-N)&gt;::valcommondivs&lt;(N-M),M&gt;::val 的实例化,即使在编译时条件已知并且永远不会采用其中一个分支。

? :替换为std::conditional_t,没有这个限制:

static const int val = std::conditional_t<N < M, commondivs<N,(M-N)>, commondivs<(N-M),M>>::val;

【讨论】:

    【解决方案2】:

    问题是条件运算符的所有操作数都会被求值,所以commondivs&lt;N,(M-N)&gt;commondivs&lt;(N-M),M&gt; 都被实例化,它们的val 被求值,然后导致递归模板实例化。

    您可以申请constexpr if 并将其放入constexpr static 成员函数中。

    如果值为true,则丢弃statement-false(如果存在),否则丢弃statement-true。

    template<int N, int M>
    struct commondivs {                                              
      constexpr static int get_val() {
        if constexpr (N<M) return commondivs<N,(M-N)>::val; // if true, the else part won't be evaluated
        else return commondivs<(N-M),M>::val;               // vice versa
      }
      static const int val = get_val();
    };
    

    LIVE

    【讨论】:

    • 已评估还是刚刚实例化?
    • @DanielMcLaury 已评估;不仅仅是实例化。
    • ::val 的值肯定必须在两个分支上生成,但这仍然是实例化(具有静态 const 成员的模板)。运行时的评估不会发生......好吧,它显然不能,因为它从不编译......
    【解决方案3】:

    三元运算符不像if constexpr:当编译器看到它时,它必须为两个分支生成代码。换句话说,要实例化模板commondivs&lt;M, N&gt;,编译器会实例化两个模板commondivs&lt;N, M - N&gt;commondivs&lt;N - M, M&gt;

    与此相反,commondiv(N, M - N)commondiv(N - M, M) 被翻译成两个函数调用。取哪一个,将在实际调用该函数时决定。

    加法。

    HolyBlackCatstd::conditional_t 给出了解决方案。这是另一个:

    template<int N, int M>
    struct commondivs {                                              
        static constexpr int min = (N < M) ? N : M;
        static constexpr int max = (N < M) ? M : N;
        static constexpr int val = commondivs<min, max - min>::val;
    };
    
    template<int N>
    struct commondivs<N, N> {
        static constexpr int val = N;
    };
    

    【讨论】:

      【解决方案4】:

      你会得到无限递归,因为:

      static const int val = (N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val;
      

      根本不是元模板编程,因为正如@Eng 所说,?: 不是constexpr

      你想看看@HolyBlackCat 的回答。

      【讨论】:

      • 没用。 ?: 不是 constexpr
      • 不,我试试。同样的无限循环。
      猜你喜欢
      • 2012-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多