テンプレートの特殊化
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 66/104。
場合によっては、generic templateがすべての型でうまく機能するとは限りません。たとえば、Cスタイルの文字列を>で比較すると、実際のテキストではなくポインターアドレスが比較されます。template specializationを使うと、その他すべての型にはgeneric versionを維持しながら、特定の型に対してカスタムのimplementationを提供できます。
full 特殊化は、特定の1つの型に対してテンプレート全体を置き換えます。空のテンプレートパラメーターリストで宣言し、具体的な型を指定します。
// プライマリテンプレート
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << std::endl;
}
};
// const char* の完全特殊化
template <>
class Printer<const char*> {
public:
void print(const char* value) {
std::cout << "String: " << value << std::endl;
}
};
Printer<int> intPrinter;
intPrinter.print(42); // 出力: 42
Printer<const char*> strPrinter;
strPrinter.print("hello"); // 出力: String: helloFunctionテンプレートは特殊化することもできます。
template <typename T>
bool isEqual(T a, T b) {
return a == b;
}
template <>
bool isEqual<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) == 0;
}
isEqual(5, 5); // 汎用版を使用
isEqual("hi", "hi"); // 特殊化版を使用コンパイラは常に最も具体的に一致するものを優先します。使用されている正確な型に対する特殊化が存在する場合は、汎用テンプレートよりもそちらが選択されます。
チャレンジ
簡単特定の型に対して動作をカスタマイズしながら、それ以外のすべての型には汎用的なフォールバックを維持できる、テンプレート特殊化の仕組みを示す型対応フォーマッターシステムを構築しましょう。
テンプレートコードを整理するために、2つのファイルを作成します。
Formatter.h:値を表示用にフォーマットする、FormatterというクラステンプレートをDefineします。genericテンプレートは任意の型で動作するようにしますが、カスタム処理が必要な型については特殊化したバージョンも作成します。Primary
Formatterテンプレートには、次のものを含めます。- 型Tの値を受け取り、
Value: <value>とprintsするformat()method
1または0ではなく、人間が読みやすいテキストをprintsする
boolのFull特殊化を作成します。format()methodは、Boolean: trueまたはBoolean: falseをprintsします。
文字列を引用符で囲む
const char*のFull特殊化を作成します。format()methodは、String: "<value>"をprintsします。
同じ型の2つの値を受け取り、
Pair: [<first>, <second>]としてまとめてprintsする、formatPairというfunctionテンプレートも作成します。各文字列を引用符で囲む
const char*用のformatPairの特殊化を作成します:Pair: ["<first>", "<second>"]- 型Tの値を受け取り、
main.cpp:4つのinputsを読み取ります(それぞれ別の行に入力します)。- integer
- double
- 文字列としてのboolean(
trueまたはfalse) - 文字列の値
適切な
Formatterオブジェクトを作成し、それぞれの型に対してformat()をCallして、フォーマッターの動作を確認します。Formatter<int>を使用してintegerをフォーマットするFormatter<double>を使用してdoubleをフォーマットするFormatter<bool>を使用してbooleanをフォーマットする(最初に文字列の入力をboolにConvertする)Formatter<const char*>を使用して文字列をフォーマットする
次に、functionテンプレートの特殊化を確認します。
- 2つのintegers(10と20)を指定して
formatPairをCallする - 2つのC-strings(
"hello"と"world")を指定してformatPairをCallする
たとえば、inputsが42、3.14、true、Helloの場合:
Value: 42
Value: 3.14
Boolean: true
String: "Hello"
Pair: [10, 20]
Pair: ["hello", "world"]genericテンプレートはintegersとdoublesを同じように処理する一方、boolとconst char*の特殊化されたバージョンはカスタムフォーマットを提供していることに注目してください。コンパイラーは、使用する各型に対して最もspecificな一致を自動的に選択します。
自分で試してみよう
#include <iostream>
#include <string>
#include "Formatter.h"
using namespace std;
int main() {
// 入力を読み取る
int intVal;
double doubleVal;
string boolStr;
string strVal;
cin >> intVal;
cin >> doubleVal;
cin >> boolStr;
cin >> strVal;
// 文字列をboolに変換する
bool boolVal = (boolStr == "true");
// TODO: Formatter<int>を作成して整数をフォーマットする
// TODO: Formatter<double>を作成してdoubleをフォーマットする
// TODO: Formatter<bool>を作成してbooleanをフォーマットする
// TODO: Formatter<const char*>を作成して文字列をフォーマットする
// ヒント: strVal.c_str()を使ってconst char*を取得する
// TODO: 2つの整数10と20でformatPairを呼び出す
// TODO: 2つのC文字列"hello"と"world"でformatPairを呼び出す
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス自分で練習してみよう: C++オンラインコンパイラ