Menu
Coddy logo textTech

関数テンプレート

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

2つの整数の最大値を見つける関数を書き、その後、double型やstring型に対しても同じロジックが必要であることに気づく場面を想像してみてください。テンプレートがなければ、それぞれの型に対してほぼ同一の関数を書くことになるでしょう。関数テンプレートは、ロジックを一度書くだけで、コンパイラが型固有のバージョンを自動的に生成するようにすることで、この問題を解決します。

関数テンプレートは、templateキーワードを使用し、その後に山括弧で囲まれたテンプレートパラメータが続きます。

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

int main() {
    std::cout << maximum(5, 3) << std::endl;       // int版を使用
    std::cout << maximum(3.14, 2.71) << std::endl; // double版を使用
    std::cout << maximum('a', 'z') << std::endl;   // char版を使用
}

コンパイラは各呼び出しを検査し、その特定の型に対応する具体的な関数を生成します。このプロセスはテンプレートのインスタンス化と呼ばれます。必要に応じて、型を明示的に指定することもできます:

std::cout << maximum<double>(5, 3.14) << std::endl;  // double型を強制します

テンプレートは複数の型パラメータを持つことができ、さらに柔軟な設計が可能になります。

template <typename T, typename U>
void printPair(T first, U second) {
    std::cout << first << ", " << second << std::endl;
}

printPair(42, "hello");    // T=int, U=const char*
printPair(3.14, 100);      // T=double, U=int

関数テンプレートはコンパイル時ポリモーフィズムを提供します。型は実行時ではなく、コードのコンパイル時に決定されます。これは仮想関数と比較して実行時のオーバーヘッドがゼロであることを意味し、テンプレートをパフォーマンスが重要なジェネリックコードに理想的なものにします。

challenge icon

チャレンジ

簡単

関数テンプレートを使用して、互換性のある任意の型で動作する再利用可能な操作を作成し、ユーティリティツールキットを構築しましょう。汎用関数をヘッダーファイルにまとめ、メインプログラムでその柔軟性を実証します。

2つのファイルを作成します:

  • MathUtils.h: 一般的な操作を実行する関数テンプレートのコレクションを定義します:

    minimum — 同じ型の2つの値を受け取り、小さい方の値を返すテンプレート関数。

    clamp — 値、下限、上限の3つのパラメータを受け取るテンプレート関数。値を範囲内に制限して返します(値が下限より小さい場合は下限を、上限より大きい場合は上限を、それ以外の場合は値自体を返します)。

    swapValues — 同じ型の2つの参照を受け取り、それらの値を入れ替えるテンプレート関数。

  • main.cpp: 6つの入力(それぞれ別の行)を読み取ります:

    1. 1つ目の整数
    2. 2つ目の整数
    3. クランプする double 値
    4. 下限 (double)
    5. 上限 (double)
    6. 文字

    以下の方法でテンプレートを実証してください:

    1. 2つの整数の最小値を見つけ、次のように出力します: Min of <a> and <b>: <result>
    2. 文字 'm' と入力された文字の最小値を見つけ、次のように出力します: Min of m and <char>: <result>
    3. double 値をクランプし、次のように出力します: Clamp <value> to [<low>, <high>]: <result>
    4. 1つ目の整数を [0, 100] の範囲にクランプし、次のように出力します: Clamp <value> to [0, 100]: <result>
    5. 2つの整数を入れ替え、次のように出力します: After swapValues: <a>, <b>

例えば、入力が 25, 10, 3.7, 1.0, 5.0, z の場合:

Min of 25 and 10: 10
Min of m and z: m
Clamp 3.7 to [1, 5]: 3.7
Clamp 25 to [0, 100]: 25
After swapValues: 10, 25

各テンプレート関数が整数、double、文字に対してシームレスに動作することに注目してください。コンパイラは、使用する型ごとに適切なバージョンを生成します。swapValues 関数は参照を介して元の変数を変更する必要があり、テンプレートが通常の関数と同様に参照パラメータで動作することを示します。

チートシート

関数テンプレートを使用すると、複数の型で動作する汎用的な関数を記述できます。templateキーワードに続けてテンプレートパラメータを使用します:

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

コンパイラは、テンプレートのインスタンス化を通じて、型固有のバージョンを自動的に生成します:

maximum(5, 3);        // intバージョンを生成
maximum(3.14, 2.71);  // doubleバージョンを生成
maximum('a', 'z');    // charバージョンを生成

必要に応じて、型を明示的に指定することもできます:

maximum<double>(5, 3.14);  // doubleバージョンを強制

テンプレートは複数の型パラメータをサポートしています:

template <typename T, typename U>
void printPair(T first, U second) {
    std::cout << first << ", " << second << std::endl;
}

printPair(42, "hello");  // T=int, U=const char*

テンプレートは、元の値を変更するための参照パラメータでも動作します:

template <typename T>
void swapValues(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

関数テンプレートは、実行時のオーバーヘッドなしでコンパイル時ポリモーフィズムを提供するため、パフォーマンスが重要な汎用コードに最適です。

自分で試してみよう

#include <iostream>
#include "MathUtils.h"
using namespace std;

int main() {
    // 入力を読み込む
    int a, b;
    double value, low, high;
    char ch;
    
    cin >> a;
    cin >> b;
    cin >> value;
    cin >> low;
    cin >> high;
    cin >> ch;
    
    // TODO: minimum テンプレートを使用して2つの整数の最小値を見つける
    // 出力: "Min of <a> and <b>: <result>"
    
    // TODO: minimum テンプレートを使用して 'm' と入力された文字の最小値を見つける
    // 出力: "Min of m and <char>: <result>"
    
    // TODO: double 型の値に対して clamp テンプレートを使用する
    // 出力: "Clamp <value> to [<low>, <high>]: <result>"
    
    // TODO: 最初の整数に対して範囲 [0, 100] で clamp テンプレートを使用する
    // 出力: "Clamp <value> to [0, 100]: <result>"
    
    // TODO: 2つの整数に対して swapValues テンプレートを使用する
    // 出力: "After swapValues: <a>, <b>"
    
    return 0;
}
quiz icon腕試し

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

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