C++/テンプレート

出典: フリー教科書『ウィキブックス(Wikibooks)』
< C++

C++のテンプレートは、汎用的なプログラムを記述するための非常に強力な機能です。テンプレートを使用することで、型に依存しないコードを一度だけ記述し、様々な型に対して再利用することができます。テンプレートは主に関数テンプレートとクラステンプレートに分けられます。

関数テンプレート[編集]

関数テンプレートは、異なるデータ型に対して同じ処理を行う関数を定義するのに使われます。例えば、2つの値を比較するmax関数を考えてみましょう。

template<typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

auto main() -> int {
    int a{5}, b{10};
    double x{5.5}, y{10.1};
    
    std::cout << max(a, b) << std::endl;  // int型のmaxを呼び出す
    std::cout << max(x, y) << std::endl;  // double型のmaxを呼び出す

    return 0;
}

この例では、max関数はテンプレート引数Tによって任意の型を受け取ることができます。コンパイラは、max関数が呼び出された際に適切な型に対して具体化された関数を生成します。

クラステンプレート[編集]

クラステンプレートは、異なるデータ型に対して同じクラス定義を使用するためのテンプレートを作成します。例えば、単純なスタッククラスをテンプレート化する場合を考えてみましょう。

template<typename T>
class Stack {
private:
    std::vector<T> elems;
public:
    void push(const T& elem) {
        elems.push_back(elem);
    }

    void pop() {
        if (elems.empty()) {
            throw std::out_of_range("Stack<>::pop(): empty stack");
        }
        elems.pop_back();
    }

    auto top() const -> T {
        if (elems.empty()) {
            throw std::out_of_range("Stack<>::top(): empty stack");
        }
        return elems.back();
    }

    auto empty() const -> bool {
        return elems.empty();
    }
};

auto main() -> int {
    Stack<int> intStack;
    Stack<std::string> stringStack;

    intStack.push(1);
    intStack.push(2);
    std::cout << intStack.top() << std::endl; // 出力: 2
    intStack.pop();
    std::cout << intStack.top() << std::endl; // 出力: 1

    stringStack.push("hello");
    stringStack.push("world");
    std::cout << stringStack.top() << std::endl; // 出力: world
    stringStack.pop();
    std::cout << stringStack.top() << std::endl; // 出力: hello

    return 0;
}

この例では、Stackクラスがテンプレート化されており、異なる型(intstd::stringなど)に対して使用することができます。

テンプレートの特殊化[編集]

テンプレートの特殊化を使うことで、特定の型に対して異なる実装を提供することができます。これは主に、特定の型に対する最適化や特別な処理が必要な場合に使用されます。

template<typename T>
class Printer {
public:
    void print(const T& value) {
        std::cout << "Generic printer: " << value << std::endl;
    }
};

// char型に対する特殊化
template<>
class Printer<char> {
public:
    void print(const char& value) {
        std::cout << "Char printer: " << value << std::endl;
    }
};

auto main() -> int {
    Printer<int> intPrinter;
    Printer<char> charPrinter;

    intPrinter.print(123);    // 出力: Generic printer: 123
    charPrinter.print('A');   // 出力: Char printer: A

    return 0;
}

この例では、Printerクラステンプレートのchar型に対する特殊化を行い、char型の値に対して特別な出力を行うようにしています。

テンプレートのパラメータパック[編集]

C++11以降では、テンプレートパラメータパックを使用することで、可変長引数を取るテンプレートを定義することができます。これにより、任意の数のテンプレート引数を扱うことができます。

template<typename... Args>
void printAll(const Args&... args) {
    (std::cout << ... << args) << std::endl; // C++17以降のFold Expression
}

auto main() -> int {
    printAll(1, 2.5, "hello", 'c'); // 出力: 12.5helloc

    return 0;
}

この例では、printAll関数が任意の数の引数を受け取り、それらをすべて標準出力に出力します。C++17のFold Expressionを使用して、可変長引数を一括処理しています。

まとめ[編集]

C++のテンプレートは、コードの再利用性を高め、異なるデータ型に対して汎用的なアルゴリズムやデータ構造を提供するための強力な機能です。関数テンプレートとクラステンプレートの基本を理解し、さらに特殊化やテンプレートパラメータパックなどの高度な機能を活用することで、柔軟で効率的なプログラムを作成することができます。