C++のテンプレート
[これまでのレッスンでは]、[私たちが作成した]関数[や]クラスは[いずれも][1つの]型しか処理できませんでした。
[しかし、もしあなたが]「
int、double、string[を処理できる]max関数」[を書こうとするなら]?テンプレート[はこの問題を解決する]――[関数]と[クラス]が[任意の]型をサポートできるようにする。
1. テンプレートとは何ですか?
(1) 1.1 [生活の中の]テンプレート*
| [生活シーン] | 番組[での対応] |
|---|---|
| [ケーキ型]([異なる味の生地を流し込める]) | テンプレート |
| Word [差し込み印刷]([1つの]テンプレート、[異なる受信者を挿入]) | テンプレート |
テンプレートの本質: [コンパイラに][使用時の]型に基づいて、[対応するバージョンの]関数[または]クラスを[自動的に生成させる]こと。
(2) 1.2 なぜテンプレートが必要なのか?
[不要]テンプレート([重複]コード):
#include iostream
// int max
int max(int a, int b) {
return a > b ? a : b;
}
// double max
double max(double a, double b) {
return a > b ? a : b;
}
// ... write N!
[用]テンプレート(コード[再利用]):
#include iostream
// ...
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << max(3, 5) << std::endl; // int
std::cout << max(3.14, 2.72) << std::endl; // double
return 0;
}
2. functiontemplate*
(1) 2.1 [基本構文]*
template <typename >
() {
// ...
}
💡 [ポイント]: typename [は] class と書くこともできます([両者は同等]ですが)、typename を使うことをお勧めします([より直感的]だからです)。
▶ サンプル 1: functiontemplate (難易度 ⭐)
#include iostream
// ...
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << "max(3, 5) = " << max(3, 5) << std::endl;
std::cout << "max(3.14, 2.72) = " << max(3.14, 2.72) << std::endl;
// ...
std::cout << "maxint(3, 5) = " << maxint(3, 5) << std::endl;
return 0;
}
出力:
max(3, 5) = 5
max(3.14, 2.72) = 3.14
maxint(3, 5) = 5
3. クラステンプレート*
(1) 3.1 [基本構文]*
template <typename >
class {
// ...
};
▶ サンプル 2: クラステンプレート Pair (難易度 ⭐⭐)
#include iostream
#include string
//:
template <typename T1, typename T2>
class Pair {
private:
T1 first;
T2 second;
public:
Pair(const T1& a, const T2& b) : first(a), second(b) {}
void print() {
std::cout << "(" << first << ", " << second << ")" << std::endl;
}
};
int main() {
Pair<int, double> p1(3, 3.14);
Pair<std::string, int> p2("Alice", 20);
p1.print(); // (3, 3.14)
p2.print(); // (Alice, 20)
return 0;
}
💡 [重要]: クラステンプレート[を使用する際は][明示的に]型を指定する必要があります([ただし]コンストラクタ[によって型が推論される]場合を除く)。
4. テンプレートの宣言と定義*
(1) 4.1 [質問]:template[はどこに置くべきか]?*
[もし]template[の宣言を] .hに、[定義を] .cppに置くと、[[リンク]エラー]が発生します!
[原因]: テンプレートは[真の]関数やクラスではなく、[それは]コンパイル時に[生成される]コード[である]「[テンプレート]」です。
- [もし宣言が]
.h、[定義が].cppである場合、コンパイラは.cppをコンパイル [する際]、[どの] タイプ [のバージョン] を生成すべきか わからない。 - [[templateが使用されている箇所]のみ、コンパイラは[どのバージョンを生成すべきか]を認識します。
(2) 4.2 [解決策]:宣言と定義を一緒にする*
[推奨される書き方]: [template]の宣言と定義をすべて.hファイル[に]記述します。
// pair.h(file)
#ifndef PAIR_H
#define PAIR_H
template <typename T1, typename T2>
class Pair {
private:
T1 first;
T2 second;
public:
Pair(const T1& a, const T2& b);
void print();
};
//!
template <typename T1, typename T2>
Pair<T1, T2>::Pair(const T1& a, const T2& b) : first(a), second(b) {}
template <typename T1, typename T2>
void Pair<T1, T2>::print() {
std::cout << "(" << first << ", " << second << ")" << std::endl;
}
#endif
💡 [ヒント]: [これは]テンプレートと[汎用]関数/クラスの[最大]の違い——テンプレートは[通常、宣言と定義が一緒に行われる]。
5. 演習:[簡単]arraytemplate*
▶ サンプル 3: [実装] Array クラステンプレート (難易度 ⭐⭐⭐)
#include iostream
#include cassert
template <typename T>
class Array {
private:
T* data;
int size;
public:
Array(int sz) : size(sz) {
data = new T[size];
}
~Array() {
delete data;
}
// ()
Array(const Array& other) : size(other.size) {
data = new T[size];
for (int i = 0; i < size; i++) {
data[i] = other.data[i];
}
}
//
T& operator(int index) {
assert(index >= 0 && index < size);
return data[index];
}
int getSize() const {
return size;
}
};
int main() {
Arrayint arr(5);
for (int i = 0; i < arr.getSize(); i++) {
arr[i] = (i + 1) * 10;
}
for (int i = 0; i < arr.getSize(); i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
return 0;
}
❓ よくある質問
Q:テンプレート[と]関数のオーバーロード[にはどのような違いがありますか]? A:> - 関数のオーバーロード:[複数の同名の]関数で、[引数の]型が[異なる](コンパイル時に[どの関数を呼び出すかが]決定される) > - 関数テンプレート:[1つの]「[型]」、コンパイラが[使用時の]型[に基づいて][対応するバージョン]を[自動的に生成する] > > [推奨]: [ロジックが完全に同じ場合]([単に]型が[異なる]場合)、[テンプレート]を使用します。[ロジックが異なる場合]は、[オーバーロード]を使用します。 Q:テンプレートは[すべての]型を[サポートできますか]? A:[できません]!テンプレートは、型が[特定の演算をサポートしていること]を[要求]します。 > >
template<typename T>> T add(T a, T b) { > return a + b; // [Tが] operator+ [をサポートしていること] [が要件] > } > > // [もし] T [がカスタム]クラスであり、[かつ] operator+ のオーバーロードがない場合、[コンパイル時に]エラー[が発生する]。 > Q:[いつ]template[を使うべきか]? A:[関数や]クラス[の][ロジックが]type[とは無関係][な場合]。 > > [典型的なシナリオ]: > - コンテナクラス(std::vector、std::array) > - アルゴリズム(std::sort、std::find) > - [ユーティリティ]関数(std::max、std::min、std::swap)
Q:templates basicsについて最も重要なことは何ですか? A:まず核心概念を理解し、その後実践的な例で練習することが重要です。
📖 まとめ
- テンプレート[関数][および]クラス[が][任意の]型をサポートできるようにする
- 関数テンプレート:
template<typename T> T foo(T x) { ... } - クラステンプレート:
template<typename T> class Foo { ... }; - テンプレートの[宣言と定義][は必ず一緒に記述すること]([通常は]
.hファイルに記述する) - テンプレートは[実際の]関数やクラスではなく、コンパイル時に[生成される]コードの「[型]」である
📝 練習問題
-
初心者(難易度 ⭐): [1つの]関数テンプレート
T min(T a, T b)を記述し、[2つの]値のうち[小さい方の]値を返す。 -
中級(難易度 ⭐⭐): [1つの]クラステンプレート
BoxTを記述し、[1つの]T型のメンバ、[および]void set(T val)[と]T get()のメンバ関数を含める。 -
上級(難易度 ⭐⭐⭐): [上記の]
Arrayクラステンプレートを[拡張]し、[追加]:
-
void push_back(const T& val)([動的拡張]) -
void pop_back() -
[用]テンプレート[を実装]し、[任意の]typeを格納できるようにする
-
関数テンプレート:ジェネリック[プログラミング]、コンパイル時インスタンス化[化]
-
テンプレートパラメータ[用] typename/class [宣言]
-
クラステンプレート:コンテナ、スマートポインタ[など、汎用的な抽象クラス]
-
テンプレートの特化:[特定の]型[に対する実装]
-
テンプレートの[利点]:コードの[再利用]、型[安全性]、[オーバーヘッドなし]
6. 🚀 次は
[「テンプレート」の基礎を学びました]。[次は] STLコンテナ([第]34[回])を学びましょう―― C++の標準ライブラリが提供する既製のコンテナを[利用]し、[自分で一から作る必要はありません]!



