c++template-meta-programmingconstexprcompile-timeif-constexpr

3 different / same ways of doing N-factorial compile time in C++


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.


Solution

  • With toy examples like this there won't be much of a difference. With more complex examples, recursive template instantiation practically has memory costs proportional to the sum of the lengths of all of the template-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.