9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

C++のテンプレートシステムを使って、コンパイル時に計算を行う技法を解説する。

「テンプレート黒魔術」とも呼ばれるけど、基本を押さえればそこまで難しくない。

TMPとは

テンプレートメタプログラミングは、テンプレートの特殊化と再帰を使ってコンパイル時に計算を行う技法です。

1. 階乗(基本)

template<int N>
struct Factorial {
    static constexpr int value = N * Factorial<N - 1>::value;
};

template<>
struct Factorial<0> {
    static constexpr int value = 1;
};

// 使用例
constexpr int fact5 = Factorial<5>::value;  // 120

2. フィボナッチ数列

template<int N>
struct Fibonacci {
    static constexpr int value = 
        Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;
};

template<>
struct Fibonacci<0> {
    static constexpr int value = 0;
};

template<>
struct Fibonacci<1> {
    static constexpr int value = 1;
};

// Fib(10) = 55

3. 型リスト

template<typename... Types>
struct TypeList {
    static constexpr size_t size = sizeof...(Types);
};

// 型リストの先頭を取得
template<typename List>
struct Front;

template<typename T, typename... Rest>
struct Front<TypeList<T, Rest...>> {
    using type = T;
};

// 使用例
using MyList = TypeList<int, double, char>;
using FirstType = Front<MyList>::type;  // int

4. 条件分岐

template<bool Condition, typename Then, typename Else>
struct If;

template<typename Then, typename Else>
struct If<true, Then, Else> {
    using type = Then;
};

template<typename Then, typename Else>
struct If<false, Then, Else> {
    using type = Else;
};

template<bool Condition, typename Then, typename Else>
using If_t = typename If<Condition, Then, Else>::type;

// sizeof(int) == 4 なら int、そうでなければ long
using Result = If_t<(sizeof(int) == 4), int, long>;

5. 素数判定

template<int N, int D>
struct IsPrimeHelper {
    static constexpr bool value = 
        (N % D != 0) && IsPrimeHelper<N, D - 1>::value;
};

template<int N>
struct IsPrimeHelper<N, 1> {
    static constexpr bool value = true;
};

template<int N>
struct IsPrime {
    static constexpr bool value = IsPrimeHelper<N, N - 1>::value;
};

// IsPrime<17>::value == true

6. 最大公約数

template<int A, int B>
struct GCD {
    static constexpr int value = GCD<B, A % B>::value;
};

template<int A>
struct GCD<A, 0> {
    static constexpr int value = A;
};

// GCD<24, 36>::value == 12

7. 累乗

template<int Base, int Exp>
struct Power {
    static constexpr int value = Base * Power<Base, Exp - 1>::value;
};

template<int Base>
struct Power<Base, 0> {
    static constexpr int value = 1;
};

// Power<2, 10>::value == 1024

8. 可変長テンプレートの再帰処理

// 合計
template<int... Ns>
struct Sum;

template<>
struct Sum<> {
    static constexpr int value = 0;
};

template<int First, int... Rest>
struct Sum<First, Rest...> {
    static constexpr int value = First + Sum<Rest...>::value;
};

// Sum<1, 2, 3, 4, 5>::value == 15

// 最大値
template<int... Ns>
struct Max;

template<int N>
struct Max<N> {
    static constexpr int value = N;
};

template<int First, int Second, int... Rest>
struct Max<First, Second, Rest...> {
    static constexpr int value = 
        Max<(First > Second ? First : Second), Rest...>::value;
};

// Max<3, 7, 2, 9, 1>::value == 9

9. 型変換ユーティリティ

template<typename T>
struct RemovePointer {
    using type = T;
};

template<typename T>
struct RemovePointer<T*> {
    using type = T;
};

template<typename T>
using RemovePointer_t = typename RemovePointer<T>::type;

// RemovePointer_t<int*> == int

実行結果

=== 階乗 ===
5! = 120
10! = 3628800

=== フィボナッチ ===
Fib(10) = 55
Fib(15) = 610

=== 型リスト ===
TypeList size: 4
Front type is int: 1

=== 素数判定 ===
IsPrime<7>: 1
IsPrime<12>: 0
IsPrime<17>: 1

=== 最大公約数 ===
GCD<24, 36>: 12
GCD<48, 18>: 6

=== 累乗 ===
2^10 = 1024
3^5 = 243

=== 合計・最大 ===
Sum<1,2,3,4,5>: 15
Max<3,7,2,9,1>: 9

TMPの利点

  1. ゼロコストの抽象化: 全てコンパイル時に計算される
  2. 型安全: コンパイル時に型チェック
  3. 最適化: ランタイムコストなし

現代C++では

C++11以降のconstexprやC++20のconceptsにより、TMPはより読みやすく書けるようになりました。

// 現代的な書き方
constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

まとめ

TMPはC++の強力な機能ですが、可読性が低下しがちです。C++11以降ではconstexprを優先し、TMPは型レベルの操作に使うのが良いでしょう。

9
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?