我什么时候应该在 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
时实例化模板,而其他分支将被丢弃,它也可以正常编译。
我一直在尝试编写一个元函数来在编译时评估权力。 我已经设法通过模板元编程来做到这一点,实现方式如下:
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
时实例化模板,而其他分支将被丢弃,它也可以正常编译。