模板元编程


38

有人可以向我解释一下,为什么第一个模板元编程方式将进入无限循环,而第二个模板正确运行。

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


2
目标是使用模板元编程。constexpr不是一个选择。
Exxul

添加了c ++ 98标记以使之成为显式constexpr选项。(它是在C ++ 11中引入的)。那确实使现有答案无效。Exxul,请明确说明您限于哪个C ++版本。
MSalters

抱歉,我删除了标签。
Exxul

Answers:


44
(N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val

此行导致两个实例化commondivs<N,(M-N)>::valcommondivs<(N-M),M>::val,即使条件是在编译时已知及分支机构的一个永远不会被取出。

替换? :std::conditional_t,没有此限制:

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

15

问题是条件运算符的所有操作数都将被求值,因此,commondivs<N,(M-N)>和都commondivs<(N-M),M>被实例化,它们的值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();
};

生活


评估还是仅实例化?
丹尼尔·麦克劳里

@DanielMcLaury评估; 不只是实例化。
songyuanyao19年

当然::val必须在两个分支上都生成的值,但这仍然是实例化(具有静态const成员的模板)。评估在运行时也不会发生......嗯,这显然不能,因为它从来没有编译...
无用

8

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

与此相反,commondiv(N, M - N)commondiv(N - M, M)被转换为两个函数调用。当实际调用该函数时,将决定采用哪一个。

加成。

HolyBlackCat通过给出了一个解决方案std::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;
};

0

您得到无限递归,因为:

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

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

您想看看@HolyBlackCat的答案。


1
这不会有帮助。?:不是constexpr
EVG

不,我尝试。相同的无限循环。
Exxul
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.