콘텐츠로 이동

T.meta

T.meta: Template metaprogramming (TMP)

Templates provide a general mechanism for compile-time programming.

Metaprogramming is programming where at least one input or one result is a type. Templates offer Turing-complete (modulo memory capacity) duck typing at compile time. The syntax and techniques needed are pretty horrendous.

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

Reason

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

???

Example, bad

enable_if

Instead, use concepts. But see How to emulate concepts if you don't have language support.

Example

??? good

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

Reason

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.

Example

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;}

Enforcement

???

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

Reason

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.

Example

??? big object / small object optimization

Enforcement

???

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

Reason

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.

Example

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);

Enforcement

  • Flag template metaprograms yielding a value. These should be replaced with constexpr functions.

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

Reason

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

Example

???

Enforcement

???

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

Reason

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.

Example

???

Enforcement

???