template metaprogramming (tsz. template metaprogrammings)
A C++ template-ek lehetővé teszik, hogy típusok vagy értékek szerint paraméterezhető kódot írjunk. Két fő típusa van:
Egy metaprogram olyan program, amely más programok szerkezetét vagy viselkedését generálja vagy módosítja. TMP esetén a C++ fordító futtatja ezt a metaprogramot fordítási időben.
Miért használjunk TMP-t?
template<int N>
struct Factorial {
static const int value = N * Factorial<N - 1>::value;
};
template<>
struct Factorial<0> {
static const int value = 1;
};
// Használat:
int main() {
int x = Factorial<5>::value; // 120
}
Ez a sablon rekurzívan kiszámítja az N!
értéket fordítási időben.
Lehetővé teszi, hogy bizonyos template-paraméterekre külön implementációt írjunk.
template<typename T>
struct IsPointer {
static const bool value = false;
};
template<typename T>
struct IsPointer<T*> {
static const bool value = true;
};
TMP nem tartalmaz ciklusokat. Ehelyett rekurziót használunk:
template<int N>
struct Sum {
static const int value = N + Sum<N - 1>::value;
};
template<>
struct Sum<0> {
static const int value = 0;
};
constexpr
(C++11+)Bár nem része a klasszikus TMP-nek, a constexpr
funkciók hasonló előnyöket adnak:
constexpr int factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
Ez runtime és compile-time időben is működhet.
Jellemző | TMP | Runtime |
---|---|---|
Futási időben hajtódik végre? | ❌ | ✅ |
Fordítási időben hajtódik végre? | ✅ | ❌ |
Kódgenerálás ideje | Fordításkor | Futáskor |
Teljesítmény overhead | Nincs | Lehet |
Hibák észlelése | Kompilációkor | Futáskor |
<type_traits>
)std::is_integral<T>::value
std::is_same<T, U>::value
std::enable_if
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type
foo(T val) {
// csak integer típusokra
}
Substitution Failure Is Not An Error: ha a sablon behelyettesítése hibát ad, a fordító nem dob hibát, hanem kizárja azt az overload-ot.
if constexpr
)template<typename T>
void printType(T val) {
if constexpr (std::is_integral<T>::value)
std::cout << "egész szám\n";
else
std::cout << "nem egész szám\n";
}
template<typename... Ts>
struct TypeList {};
template<typename T, typename... Ts>
struct Length<TypeList<T, Ts...>> {
static const int value = 1 + Length<TypeList<Ts...>>::value;
};
template<>
struct Length<TypeList<>> {
static const int value = 0;
};
Ez a példa kiszámítja egy variadic sablon típuslistájának hosszát fordítási időben.
std::vector
, std::map
, std::function
is heavily template-based.
constexpr
vs. classic TMPA modern C++ (C++14–C++20) constexpr
, if constexpr
, és concepts
bevezetésével sok TMP használat egyszerűbbé vált, például:
template<typename T>
constexpr bool is_pointer_v = std::is_pointer<T>::value;
A klasszikus TMP és a modern constexpr
gyakran együtt használhatók.
A C++ template metaprogramming:
constexpr
, if constexpr
, concepts
) barátságosabbá és praktikusabbá tették