我什么时候应该在 constexpr 模板函数中使用 if constexpr 而不是常规 if ?

When should I be using if constexpr as apposed to a regular if in a constexpr template function?

我一直在尝试编写一个元函数来在编译时评估权力。 我已经设法通过模板元编程来做到这一点,实现方式如下:

template<int A, int B>
struct pow {
    static constexpr int value = (B % 2 == 0) ?
                                 pow<A, B / 2>::value * pow<A, B / 2>::value :
                                 A * pow<A, B - 1>::value;
};
template<int A>
struct pow<A, 1> {
    static constexpr int value = A;
};

但是,出于某种原因,为什么我尝试使用模板函数,但它无法编译,因为它超过了函数定义的最大递归深度:

template<int a, int b>
constexpr int template_power() {
    return (b == 0) ?
           1 : ((b % 2 == 0) ?
                template_power<a, b / 2>() * template_power<a, b / 2>() :
                a * template_power<a, b - 1>());
}

这告诉我,不知何故,条件语句在编译时没有得到正确的评估,但我看不出这个和以前的实现有什么区别。

另一方面,这个 constexpr 函数编译:

int constexpr power(int a, int b) {
    return (b == 0) ?
           1 : ((b % 2 == 0) ?
                power(a, b / 2) * power(a, b / 2) :
                a * power(a, b - 1));

此外,如果我使用 if constexpr 重构我的模板函数,它会按预期进行编译。

template<int a, int b>
int constexpr template_power() {
    if constexpr(b == 0)
        return 1;
    else
        if constexpr(b % 2 == 0)
            return template_power<a,b/2>() * template_power<a, b/2>();
    else
        return a * template_power<a, b - 1>();
}

我如何理解这一切?

我的猜测是,对于函数模板,条件语句在决定选择什么之前评估所有分支,这导致它生成未说明的函数(例如当 b 为负数时)。如果是这样,为什么条件语句会针对模板结构和 constexpr 函数正确评估,而不是模板函数?

逐字错误消息是这样的:

[path]/main.cpp: In instantiation of 'constexpr int template_power() [with int a = 2; int b = -896]':
[path]/main.cpp:42:45:   recursively required from 'constexpr int template_power() [with int a = 2; int b = -1]'
[path]/main.cpp:41:41:   recursively required from 'constexpr int template_power() [with int a = 2; int b = 2]'
[path]/main.cpp:41:41:   required from 'constexpr int template_power() [with int a = 2; int b = 5]'
[path]/main.cpp:47:43:   required from here
[path]/main.cpp:41:41: fatal error: template instantiation depth exceeds maximum of 900 (use '-ftemplate-depth=' to increase the maximum)
   41 |                 template_power<a, b / 2>() * template_power<a, b / 2>() :
  • 在您的第一个示例中,由于三元运算符是在运行时评估的(就像常规 if 语句一样),它无法停止模板的实例化,并且由于您的模板具有出口没有专门化,所有个分支将被实例化,这使得它无限递归。

  • 在你的第二个例子中,因为没有涉及模板,所以没有模板的实例化,它编译。

  • 在您的第三个示例中,由于 if constexpr 只有 在表达式求值为 true 时实例化模板,而其他分支将被丢弃,它也可以正常编译。