テンプレートメタプログラミング#
#include <cstdint>
template <uint64_t N> struct Fact {
enum { Value = N * Fact<N - 1>::Value };
};
template <> struct Fact<1> {
enum { Value = 1 };
};
template <uint64_t N> struct Fib {
enum { Value = Fib<N - 1>::Value + Fib<N - 2>::Value };
};
template <> struct Fib<1> {
enum { Value = 1 };
};
template <> struct Fib<2> {
enum { Value = 2 };
};
template <uint64_t base, uint64_t exp> struct Pow {
enum { Value = base * Pow<base, exp - 1>::Value };
};
template <uint64_t base> struct Pow<base, 1> {
enum { Value = base };
};
int main() {
auto val1 = Fact<10>::Value;
auto val2 = Fib<20>::Value;
auto val3 = Pow<2, 10>::Value;
return 0;
}
上記のコードスニペットでは、テンプレートを使用してコンパイル時の計算を実現しています(C++11
以降でサポート)。
constexpr#
C++14
からは、constexpr
を使用して関数を修飾することができるようになりました。これにより、定数を関数に渡すとコンパイラで計算され、変数を渡すと実行時に計算されます。これは、生成されるアセンブリコードからも確認できます。
#include <cstdint>
constexpr auto fact(uint64_t n) {
if (n == 1) {
return n;
}
return fact(n-1) * n;
}
int main() {
constexpr auto val = fact(10);
return 0;
}
fact(int):
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], edi
cmp DWORD PTR [rbp-4], 1
jne .L2
mov eax, DWORD PTR [rbp-4]
jmp .L3
.L2:
mov eax, DWORD PTR [rbp-4]
sub eax, 1
mov edi, eax
call fact(int)
imul eax, DWORD PTR [rbp-4]
.L3:
leave
ret
main:
push rbp
mov rbp, rsp
sub rsp, 32
mov DWORD PTR [rbp-20], edi
mov QWORD PTR [rbp-32], rsi
mov DWORD PTR [rbp-4], 3628800
mov eax, DWORD PTR [rbp-20]
mov edi, eax
call fact(int)
mov DWORD PTR [rbp-8], eax
mov eax, 0
leave
ret
constexpr とテンプレートメタプログラミングの比較#
#include <cstdint>
constexpr auto fib(uint64_t n) {
if (n <= 1) {
return 1;
}
return fib(n-1) + fib(n-2);
}
int main(int argc, char *argv[]) {
constexpr auto val = fib(40);
return 0;
}
constexpr
の記述方法を使用すると、コンパイラエラーが発生する可能性があります。これは、この関数が多くの重複計算を行うためです。
一方、以下のテンプレートメタプログラミングの記述方法を使用すると、コンパイラがメモ化を行うため、重複計算が省略されます。
将来のコンパイラでは、constexpr
関数のメモ化がサポートされる可能性があります。
template<uint64_t N> struct Fib {
enum { Value = Fib<N-1>::Value + Fib<N-2>::Value };
};
template<> struct Fib<1> {
enum { Value = 1 };
};
template<> struct Fib<0> {
enum { Value = 1};
};
int main(int argc, char *argv[]) {
auto val = Fib<40>::Value;
return 0;
}
constexpr if#
C++17
からは、constexpr if
がサポートされるようになりました。これにより、テンプレートメタプログラミングの記述方法を簡略化することができ、初期化のインスタンス化を省略することができます。これにより、複雑な計算もサポートされるようになります。なぜなら、テンプレートメタプログラミングはメモ化を行うからです。
#include <cstdint>
template<uint64_t N> struct Fib {
static constexpr uint64_t Value = []{
if constexpr (N <= 1) {
return 1;
} else {
return Fib<N-1>::Value + Fib<N-2>::Value;
}
}();
};
int main(int argc, char *argv[]) {
constexpr auto val = Fib<40>::Value;
return 0;
}