问题
I am trying to play with template metaprogramming, constexpr and if constexpr and have come up with 3 different ways of doing a N-recursive / N-factorial operation.
All three examples are some I've found here on SO or by searching on the net - and then modified it, so they do the same
The first example is using template metaprogramming: example 1
template<int N>
struct NGenerator
{
static const int result = N + NGenerator<N-1>::result;
};
template<>
struct NGenerator<0>
{
static const int result = 1;
};
static int example1 = NGenerator<5>::result;
The second one is still using a template, but I've thrown a constexpr in: example 2
template<int N>
constexpr int example2()
{
return N + example2<N - 1>();
}
template<>
constexpr int example2<0>()
{
return 1;
}
static int ex2 = example2<5>();
The third one, is where I've removed the template and "only" use constexpr: example 3
constexpr int generator(int n)
{
return (n <= 1) ? 1 : n + generator(n - 1);
}
static int ex3 = generator(5);
In my opinion all three do the same - when the input number is a compile time constant. All three are recursive, all three work compile-time.
My question is - what is the difference between the three? Which one is most preferable?
And lastly - I would like to implement the "if constexpr" but haven't been able to, so my workaround has been to do the "if-statement" in example 3 - which isn't really a true if-statement, but the closest I could get to a compile-time if - if it is in any way the same, which I am not sure of.
回答1:
With toy examples like this there won't be much of a difference. With more complex examples, recursove template instantiation practically has memory costs proportional to the sum of the lengths of all of the templates instantiated names, including arguments. This is really easy to blow up.
In my experience constexpr
functions tend to compile faster.
All 3 will be likely memoized by the compiler; but the constexpr non-template function will spew way fewer symbols (noise).
Better than all of these would be a constexpr function with a loop.
Compilers are free to do most of the options at runtime as you did not insist on compile time evaluation. Replace static int
with constexpr int
.
回答2:
Example 1 is guaranteed to be done at compile time.
constexpr
functions (example2 and example3) can be called in non constexpr context and so be computed at runtime (compiler might still optimize it to compute it at compile time withas-if rule).
I would do something like:
constexpr std::size_t factorial(std::size_t n)
{
std::size_t res = 1;
for (std::size_t i = 1; i < n; ++i) {
res *= i;
}
return res;
}
template <std::size_t N>
static constexpr std::size_t Factorial = factorial(5);
static std::size_t ex4 = Factorial<5>;
C++20 adds consteval
to only allow constexpr evaluation.
来源:https://stackoverflow.com/questions/59522226/3-different-same-ways-of-doing-n-factorial-compile-time-in-c