Menu
Coddy logo textTech

テンプレートの特殊化

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 66/104。

時として、汎用的なテンプレートがすべての型に対してうまく機能しないことがあります。例えば、Cスタイルの文字列を > で比較すると、実際のテキストではなくポインタのアドレスが比較されます。テンプレートの特殊化を使用すると、他のすべての型に対しては汎用バージョンを維持しつつ、特定の型に対してカスタム実装を提供できます。

完全特殊化は、特定の型に対してテンプレート全体を置き換えます。空のテンプレートパラメータリストを使用して宣言し、具体的な型を指定します:

// 基本テンプレート
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: hello

関数テンプレートも特殊化することができます:

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");        // 特殊化されたバージョンを使用

コンパイラは常に最も具体的な一致を優先します。使用されている正確な型に対して特殊化が存在する場合、汎用テンプレートよりもそれが優先して選択されます。

challenge icon

チャレンジ

簡単

型を認識するフォーマッタシステムを構築しましょう。これは、テンプレートの特殊化によって、特定の型に対して動作をカスタマイズしつつ、それ以外のすべての型に対しては汎用的なフォールバックを維持する方法を示すものです。

テンプレートコードを整理するために、2つのファイルを作成します:

  • Formatter.h: 表示用に値をフォーマットする Formatter というクラステンプレートを定義します。汎用テンプレートはあらゆる型で動作する必要がありますが、カスタム処理が必要な型については特殊化されたバージョンも作成します。

    プライマリ Formatter テンプレートには以下を含める必要があります:

    • 型 T の値を受け取り、Value: <value> と出力する format() メソッド。

    1 または 0 の代わりに人間が読めるテキストを出力する、bool 用の完全特殊化を作成します:

    • format() メソッドは、Boolean: true または Boolean: false と出力する必要があります。

    文字列を引用符で囲む、const char* 用の完全特殊化を作成します:

    • format() メソッドは、String: "<value>" と出力する必要があります。

    また、同じ型の2つの値を受け取り、それらをまとめて Pair: [<first>, <second>] として出力する formatPair という関数テンプレートも作成します。

    各文字列を引用符で囲む const char* 用の formatPair の特殊化を作成します:Pair: ["<first>", "<second>"]

  • main.cpp: 4つの入力(それぞれ別々の行)を読み取ります:
    1. 整数
    2. double型の数値
    3. 文字列としての真偽値("true" または "false"
    4. 文字列の値

    適切な Formatter オブジェクトを作成し、各型に対して format() を呼び出すことで、フォーマッタの実演を行います:

    1. Formatter<int> を使用して整数をフォーマットする
    2. Formatter<double> を使用して double をフォーマットする
    3. Formatter<bool> を使用して真偽値をフォーマットする(まず文字列入力を bool に変換してください)
    4. Formatter<const char*> を使用して文字列をフォーマットする

    次に、関数テンプレートの特殊化を実演します:

    1. 2つの整数 10 と 20 を指定して formatPair を呼び出す
    2. 2つのC言語スタイルの文字列 "hello""world" を指定して formatPair を呼び出す

例えば、入力が 423.14trueHello の場合:

Value: 42
Value: 3.14
Boolean: true
String: "Hello"
Pair: [10, 20]
Pair: ["hello", "world"]

汎用テンプレートが整数と double を同じように処理する一方で、boolconst char* の特殊化されたバージョンがカスタムフォーマットを提供していることに注目してください。コンパイラは、使用する各型に対して最も具体的な一致を自動的に選択します。

チートシート

テンプレートの特殊化を使用すると、他のすべての型に対しては汎用的なバージョンを維持しつつ、特定の型に対してカスタム実装を提供できます。

完全特殊化の構文

空のテンプレートパラメータリスト template <> を使用して特殊化を宣言し、具体的な型を指定します。

// 基本テンプレート
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;
    }
};

関数テンプレートの特殊化

関数テンプレートも特殊化できます。

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;
}

コンパイラの選択

コンパイラは常に最も具体的な一致を優先します。使用されている正確な型に対して特殊化が存在する場合、汎用テンプレートよりもその特殊化が選択されます。

自分で試してみよう

#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> を作成し、浮動小数点数をフォーマットする
    
    // TODO: Formatter<bool> を作成し、真偽値をフォーマットする
    
    // TODO: Formatter<const char*> を作成し、文字列をフォーマットする
    // ヒント: const char* を取得するには strVal.c_str() を使用する
    
    // TODO: 2つの整数 10 と 20 を指定して formatPair を呼び出す
    
    // TODO: 2つのC言語形式の文字列 "hello" と "world" を指定して formatPair を呼び出す
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン