I've been trying to write a metafuncion to evaluate powers at compile time. I have managed to do it with template metaprogramming, implemented as such:
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;
};
But, for some reason, why I try to use template functions, this doesn't compile, as it exceeds maximum recursion depth for function definition:
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>());
}
What this tells me is that somehow, the conditional statements don't get properly evaluated at compile time, but I don't see any difference between this and the previous implementation.
On the other hand, this constexpr
function does compile:
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));
And, furthermore, if I refactor my template function using if constexpr
, it does compile as intended.
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>();
}
How do I make sense of all of this?
My guess is that for the function template, the conditional statement evaluates all the branches before deciding what to choose, which causes it to generate unaccounted for functions (such as when b
is negative). If that is the case, why do the conditional statements get properly evaluated for the template struct and the constexpr
function, but not the template function?
The verbatim error message is this:
[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>() :
CodePudding user response:
In your first example, since the ternary operator is evaluated at runtime (just like a regular
if
statement), it cannot stop the instantiation of the template, and since your template has no specialization for the exit, all branches will be instantiated, which makes it recursive infinitely.In your second example, since there is no template involved, there is no instantiation of the template and it compiles.
In your third example, since
if constexpr
only instantiates the template when the expression evaluates totrue
, and the other branches will be discarded, it compiles fine as well.