テンプレートメタプログラミング
[第]46[課では]スマートポインタの[応用]について学びました。
[さて]、[これから]C++の「[黒魔術]」――テンプレートメタプログラミング――について取り上げます。
テンプレートメタプログラミングとは、コンパイル時にコードを実行する技術であり、実行時のパフォーマンスを大幅に向上させることができる。
1. テンプレートメタプログラミング[概要]
(1) 1.1 テンプレートメタプログラミングとは何か?
テンプレートメタプログラミング(Template Metaprogramming、TMP)とは、コンパイル****段階において、テンプレートを利用して計算を実行する技術である。
[特徴]:
- [コンパイル時に]完了し、実行時[オーバーヘッドなし]
- コード[複雑]、コンパイル時間[長い]
- エラー[情報が読みづらい]
(2) 1.2 [なぜ]テンプレートメタプログラミングを使うのか?
| [強み] | [説明] |
|---|---|
| パフォーマンス | コンパイル時[計算]、実行時[高速] |
| type[安全] | compile[期]type[チェック] |
| generic | [実際の]genericcode |
2. 計算[期計算]
(1) 2.1 コンパイル[階乗]
例:[用]テンプレート[階乗の計算] (難易度 ⭐⭐⭐)
▶ サンプル 1: codeexample (難易度 ⭐)
#include iostream
// ...
template<int N>
struct Factorial {
static const int value = N * FactorialN-1::value;
};
//:
template<>
struct Factorial<0> {
static const int value = 1;
};
int main() {
std::cout << "5! = " << Factorial<5>::value << std::endl; // 120
//,
return 0;
}
▶ サンプル 2: codeexample (難易度 ⭐)
💡 ヒント:
- [テンプレートの特化]を[再帰の][終了]条件として[用いる]
static const int value[是]コンパイル[期]定数
(2) 2.2 constexpr関数(C++11、[推奨])
C++11ではconstexprが[導入され]、[これにより]コンパイル[時の計算がより簡単になった]。
例:[用]constexpr[階乗の計算] (難易度 ⭐⭐)
#include iostream
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int result = factorial(5); // ...
std::cout << "5! = " << result << std::endl; // 120
return 0;
}
💡 ヒント:
constexprはテンプレートメタプログラミングよりも[より簡潔]であり、[優先して]constexprを使用する
3. type[抽出]
(1) 3.1 「タイプ[萃取]」とは何か?
type[萃取](Type Traits)とは、コンパイル[時に][型情報を照会または変更][する]技術のことです。
例:[用]type_traits[判断]type (難易度 ⭐⭐)
#include iostream
#include type_traits
int main() {
std::cout << std::is_integralint::value << std::endl; // 1(true)
std::cout << std::is_integraldouble::value << std::endl; // 0(false)
std::cout << std::is_pointer<int*>::value << std::endl; // 1(true)
return 0;
}
(2) 3.2 [カスタム]タイプ[抽出]
例:[存在の有無を判断する]メンバー関数([難易度]⭐⭐⭐⭐)
#include iostream
#include type_traits
// serialize
template<typename T>
struct has_serialize {
private:
template<typename U>
static auto test(int) -> decltype(std::declvalU().serialize(), std::true_type{});
template<typename U>
static std::false_type test(...);
public:
static const bool value = decltype(testT(0))::value;
};
struct Person {
void serialize() {}
};
int main() {
std::cout << has_serializePerson::value << std::endl; // 1(true)
std::cout << has_serializeint::value << std::endl; // 0(false)
return 0;
}
4. SFINAE
(1) 4.1 SFINAEとは何か?
SFINAE(Substitution Failure Is Not An Error):テンプレートパラメータの[置換に失敗しても][エラーとはみなされない]。コンパイラは[他の]オーバーロードを試みる。
[用途]: [typeの]特性[に基づいて][異なる]functionoverloadを選択する。
(2) 4.2 例:SFINAE[選択]オーバーロード([難易度]⭐⭐⭐⭐)
#include iostream
#include type_traits
#include string
// 1:
template<typename T>
typename std::enable_if<std::is_integralT::value, std::string>::type
toString(T value) {
return std::to_string(value) + "()";
}
// 2:
template<typename T>
typename std::enable_if<!std::is_integralT::value, std::string>::type
toString(T value) {
return "...";
}
int main() {
std::cout << toString(42) << std::endl; // 42 ()
std::cout << toString(3.14) << std::endl; // ...
return 0;
}
5. [可変引数]template
(1) 5.1 [可変引数]テンプレートとは何か?
[可変引数]テンプレート(Variadic Template)は、[任意の数の]引数を[受け取る]ことを[許可]します。
例:[任意の数の引数を表示する] (難易度 ⭐⭐⭐)
#include iostream
// ...
void print() {
std::cout << std::endl;
}
// ...
template<typename T, typename... Args>
void print(T first, Args... rest) {
std::cout << first << " ";
print(rest...); // ...
}
int main() {
print(1, 2.5, "hello", 'a');
// output:1 2.5 hello a
return 0;
}
6. C++17[折りたたみ式]
基本的な使い方
C++17では、[フォールド式](Fold Expression)が[導入され]、[可変引数のテンプレート]が簡素化されました。
例:[合計] (難易度 ⭐⭐)
#include iostream
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // ...
}
int main() {
std::cout << sum(1, 2, 3, 4, 5) << std::endl; // 15
return 0;
}
❓ よくある質問
Q:テンプレートメタプログラミングは[難しいですか]? A:[難しい]です。[まずは][基礎]となるテンプレートを習得してから、[メタプログラミングを学ぶ]ことをお勧めします。[ほとんどの]プロジェクトでは、メタプログラミングは[必要ありません]。
Q:[いつ]テンプレートメタプログラミングを使うのか? A:- [ライブラリの作成]([例:]STL) - [極限の]パフォーマンスが求められる場合 - [コンパイル時の]型チェックが必要な場合
constexpr、[多くの]コンパイル[時の計算が] constexpr関数[を用いて]実現できるようになりました。Q:template metaprogrammingについて最も重要なことは何ですか? A:まず核心概念を理解し、その後実践的な例で練習することが重要です。
📖 まとめ
| [知識ポイント] | [要点] |
|---|---|
| compile[期間計算] | templaterecursion[または]constexpr |
| タイプ[萃取] | type_traits [庫] |
| SFINAE | [type]に基づいて[オーバーロード]を選択 |
| [可変引数]template | [任意の数の引数を受け取る] |
| [折りたたみ式] | C++17、[可変引数の簡略化] |
📝 練習問題
-
**初心者(難易度 ⭐):[階乗を計算する] constexpr 関数を[作成し]、[コンパイル時に]値を[求める]。[static_assert 内で]結果を[検証する]。
-
**中級(難易度 ⭐⭐):[std::enable_if を使用して]、[T が整数型である場合にのみ有効になる]関数テンプレートを[実装する]。[浮動小数点型に対しては]コンパイル時に[エラー]が発生するようにする。
-
**上級(難易度 ⭐⭐⭐):[可変引数を使用して]template[として] print_all 関数を実装し、[任意の個数および]type[の引数]を受け取り、[1つずつ]出力するようにしてください。[ヒント]:[再帰を使用して](C++17)。
- テンプレートメタプログラミング:コンパイル時[計算]、実行時[オーバーヘッドなし]
- constexpr compile[求]value
- SFINAE:[置換に失敗したわけではない]エラー
- type[抽出](type traits)[検索]type[属性]
- [可変引数]template[任意の数の引数を処理]
[次のレッスン]:C++17/20[新機能](#48)



