Cpp Notes

T.meta

T.meta: Template metaprogramming (TMP)

T.120: Use template metaprogramming only when you really need to

  • Template metaprogramming is hard to get right, slows down compilation, and is often very hard to maintain.
  • However, there are real-world examples where template metaprogramming provides better performance than any alternative short of expert-level assembly code.
  • Also, there are real-world examples where template metaprogramming expresses the fundamental ideas better than run-time code.
  • For example, if you really need AST manipulation at compile time (e.g., for optional matrix operation folding) there might be no other way in C++.
  • Example, bad: enable_if. Instead, use concepts. But see How to emulate concepts if you don't have language support.
  • Alternative: If the result is a value, rather than a type, use a constexpr function.
  • Note: If you feel the need to hide your template metaprogramming in macros, you have probably gone too far.

T.121: Use template metaprogramming primarily to emulate concepts

  • Where C++20 is not available, we need to emulate them using TMP.
  • Use cases that require concepts (e.g. overloading based on concepts) are among the most common (and simple) uses of TMP.
template <typename Iter>
/*requires*/ enable_if<random_access_iterator<Iter>, void>
advance(Iter p, int n) {
    p += n;
}

template <typename Iter>
/*requires*/ enable_if<forward_iterator<Iter>, void>
advance(Iter p, int n) {
    assert(n >= 0);
    while (n--)
        ++p;
}
  • Note: Such code is much simpler using concepts:
void advance(random_access_iterator auto p, int n) { p += n; }
void advance(forward_iterator auto p, int n) { assert(n >= 0); while (n--) ++p;}

T.122: Use templates (usually template aliases) to compute types at compile time

  • Template metaprogramming is the only directly supported and half-way principled way of generating types at compile time.
  • Note: "Traits" techniques are mostly replaced by template aliases to compute types and constexpr functions to compute values.

T.123: Use constexpr functions to compute values at compile time

  • A function is the most obvious and conventional way of expressing the computation of a value.
  • Often a constexpr function implies less compile-time overhead than alternatives.
  • Note: "Traits" techniques are mostly replaced by template aliases to compute types and constexpr functions to compute values.
template<typename T>
    // requires Number<T>
constexpr T pow(T v, int n)   // power/exponential
{
    T res = 1;
    while (n--) res *= v;
    return res;
}

constexpr auto f7 = pow(pi, 7);

T.124: Prefer to use standard-library TMP facilities

  • Facilities defined in the standard, such as conditional, enable_if, and tuple, are portable and can be assumed to be known.

T.125: If you need to go beyond the standard-library TMP facilities, use an existing library

  • Getting advanced TMP facilities is not easy and using a library makes you part of a (hopefully supportive) community.
  • Write your own "advanced TMP support" only if you really have to.