I can see constexpr is winning more and more territory and one of the arguments for its use over template metaprogramming (TMP) is performance. My questions are:
Are there any examples of performance comparisons in this regard?
And why not replace TMP with constexpr if it is better?
TMP guarantees that it is run compile-time, but that can be done with constexpr too. Constexpr handles floating point numbers, which TMP doesn't and for all the examples I've seen, constexpr functions that replace TMP are easier to read and use less lines of code. Also constexpr allows conditions and loops while TMP only handles recursive "simulations" of loops.
I've asked in here earlier (link) about a related question - where I have 3 different ways of doing metaprogramming - here the constexpr function-way is the preferred..
constexpr function are generally more readable as they are regular functions.
Caveats that it can be used in runtime even with compile time constant argument (at call site).
constexpr int f(int n) { /*...*/ }
constexpr int f5 = f(5); // Compile time.
int f4 = f(4); // Runtime time (compiler can optimize as for non-constexpr functions though)
For runtime performance, there are similar when used in constant expression, as all is done at compile time.
For compile time performance, issue of TMP is the number of instantiations whereas constexpr function (with regular parameter) generally use less instantiation, So TMP makes generally compilation longer.
But, in case memoization is required (as for Fibonacci with recursive implementation)
constexpr std::size_t fibonacci(std::size_t n)
{
if (n < 2) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
template <std::size_t N> struct Fibonacci
{
constexpr std::size_t value = Fibonacci<N - 2>::value + Fibonacci<N - 1>::value;
};
template <> struct Fibonacci<0>
{
constexpr std::size_t value = 1;
};
template <> struct Fibonacci<1>
{
constexpr std::size_t value = 1;
};
TMP should reuse instantiation whereas constexpr function might recompute it each time. In that case TMP will compile faster.
And why not replace TMP with constexpr if it is better?
First, we won't drop TMP which is used, for retro-compatibility.
Then, TMP is generally more adapted to return type or several types.
template <typename T> struct add_pointer
{
using type = T*;
};
using int_ptr = add_pointer<int>::type;
whereas constexpr would require decltype and/or std::declval
template <typename T> struct Tag { using type = T; };
constexpr Tag<T*> add_pointer(Tag<T>) {return {}; }
using int_ptr = decltype(add_pointer(Tag<int>{})::type;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With