Recursion using template meta programming
(N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val
This line causes instantiation of both commondivs<N,(M-N)>::val
and commondivs<(N-M),M>::val
, even if the condition is known at compile time and one of the branches will never be taken.
Replace ? :
with std::conditional_t
, which doesn't have this limitation:
static const int val = std::conditional_t<N < M, commondivs<N,(M-N)>, commondivs<(N-M),M>>::val;
The ternary operator is not like if constexpr
: when a compiler sees it, it has to generate code for both branches. In other words, to instantiate a template commondivs<M, N>
, a compiler instantiates both templates commondivs<N, M - N>
and commondivs<N - M, M>
.
In contrast to that, commondiv(N, M - N)
and commondiv(N - M, M)
are translated into two function calls. Which one is taken, will be decided when the function is actually called.
Addition.
HolyBlackCat gave a solution with std::conditional_t
. Here is another one:
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;
};
The problem is all the operands of conditional operator will be evaluated, so both commondivs<N,(M-N)>
and commondivs<(N-M),M>
get instantiated and their val
get evaluated and then leads to recursive template instantiation.
You can apply constexpr if and put it in a constexpr
static
member function.
If the value is
true
, then statement-false is discarded (if present), otherwise, statement-true is discarded.
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