my.code(); Logomy.code();
C++-9.テンプレート

my.code(); Logomy.code();

  • C++
    • 0.C++の世界へようこそ
    • 1.型システムと制御構造
    • 2.データ集合とモダンな操作
    • 3.ポインタとメモリ管理
    • 4.関数と参照渡し
    • 5.プロジェクトの分割とビルド
    • 6.クラスの基礎
    • 7.クラスを使いこなす
    • 8.継承とポリモーフィズム
    • 9.テンプレート
    • 10.STL ①:コンテナ
    • 11.STL ②:アルゴリズムとラムダ式
    • 12.RAIIとスマートポインタ
    • ⚡Sandbox
  • JavaScript
    • 0.JavaScriptへようこそ
    • 1.基本構文とデータ型
    • 2.制御構文
    • 3.関数とクロージャ
    • 4.'this'の正体
    • 5.オブジェクトとプロトタイプ
    • 6.クラス構文
    • 7.配列とイテレーション
    • 8.非同期処理①: Promise
    • 9.非同期処理②: Async/Await
    • ⚡Sandbox
  • Python
    • 0.環境構築と基本思想
    • 1.基本構文とデータ型
    • 2.リスト、タプル、辞書、セット
    • 3.制御構文と関数
    • 4.モジュールとパッケージ
    • 5.オブジェクト指向プログラミング
    • 6.ファイルの入出力とコンテキストマネージャ
    • 7.例外処理
    • 8.ジェネレータとデコレータ
    • ⚡Sandbox
  • Ruby
    • 0.rubyの世界へようこそ
    • 1.基本構文とデータ型
    • 2.制御構造とメソッド定義
    • 3.すべてがオブジェクト
    • 4.コレクション (Array, Hash, Range)
    • 5.ブロックとイテレータ
    • 6.クラスとオブジェクト
    • 7.モジュールとMix-in
    • 8.Proc, Lambda, クロージャ
    • 9.標準ライブラリの活用
    • 10.テスト文化入門
    • 11.メタプログラミング入門
    • ⚡Sandbox
  • Rust
    • 0.Rustの世界へようこそ
    • 1.基本構文と「不変性」
    • 2.関数と制御フロー
    • 3.所有権
    • 4.借用とスライス
    • 5.構造体とメソッド構文
    • 6.列挙型とパターンマッチ
    • 7.モジュールシステムとパッケージ管理
    • 8.コレクションと文字列
    • 9.エラーハンドリング
    • 10.ジェネリクスとトレイト
    • 11.ライフタイム
    • ⚡Sandbox
  • TypeScript
    • 0.TypeScriptへようこそ
    • 1.基本的な型と型推論
    • 2.オブジェクト、インターフェース、型エイリアス
    • 3.関数の型定義
    • 4.型を組み合わせる
    • 5.ジェネリクス
    • 6.クラスとアクセス修飾子
    • 7.非同期処理とユーティリティ型
    • ⚡Sandbox
my.code(); Logomy.code();

環境構築不要、その場で実践。

ut-code / my-code

Copyright © 2026 ut.code();

my.code(); について
コード実行環境についてAI質問機能についてライセンスお問い合わせ
ut.code(); について
公式ウェブサイト公式 𝕏 アカウント

第9章: テンプレートによる汎用プログラミング

これまでの章では、intやdouble、あるいは自作のCarクラスのように、特定の型に対して処理を行う関数やクラスを作成してきました。しかし、プログラムが複雑になるにつれて、「型は違うけれど、行いたい処理は全く同じ」という状況が頻繁に発生します。例えば、2つの値の大きい方を返すmaxという関数を考えてみましょう。

int max_int(int a, int b) {
    return (a > b) ? a : b;
}

double max_double(double a, double b) {
    return (a > b) ? a : b;
}

このように、型ごとに同じロジックの関数をいくつも用意するのは非効率的ですし、バグの温床にもなります。

この問題を解決するのがテンプレートです。テンプレートを使うと、具体的な型を "仮引数" のように扱い、様々な型に対応できる関数やクラスの「設計図」を作ることができます。このような、型に依存しないプログラミングスタイルをジェネリックプログラミング(汎用プログラミング)と呼びます。

関数テンプレート: intでもdoubleでもstringでも動く関数を作る

関数テンプレートを使うと、先ほどのmax関数の問題をエレガントに解決できます。

ファイルを編集:function_template_intro.cpp
#include <iostream>
#include <string>

// Tという名前で型を仮引数として受け取るテンプレートを宣言
template <typename T>
T max_value(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    // int型でmax_valueを呼び出す
    std::cout << "max(10, 20) = " << max_value(10, 20) << std::endl;

    // double型でmax_valueを呼び出す
    std::cout << "max(3.14, 1.41) = " << max_value(3.14, 1.41) << std::endl;

    // std::string型でも動作する!
    std::string s1 = "world";
    std::string s2 = "hello";
    std::cout << "max(\"world\", \"hello\") = " << max_value(s1, s2) << std::endl;

    return 0;
}
ブラウザ上で動作するcppの実行環境です。
左上の実行ボタンを押して、このページ内のfunction_template_intro.cppに書かれている内容を実行します。
max(10, 20) = 20
max(3.14, 1.41) = 3.14
max("world", "hello") = world

テンプレートの仕組み

template <typename T>という部分が、この関数がテンプレートであることを示しています。

  • template <...>: テンプレートの宣言を開始します。
  • typename T: Tという名前の「型引数」を定義しています。typenameの代わりにclassと書くこともできますが、意味は同じです。Tは、このテンプレートが実際に使われるときに具体的な型(intやdoubleなど)に置き換えられます。

main関数でmax_value(10, 20)のように呼び出すと、コンパイラは引数の型がintであることから、Tをintだと自動的に判断します(これをテンプレート引数推論と呼びます)。そして、内部的に以下のようなint版の関数を生成してくれるのです。

// コンパイラが内部的に生成するコードのイメージ
int max_value(int a, int b) {
    return (a > b) ? a : b;
}

同様に、doubleやstd::stringで呼び出されれば、それぞれの型に対応したバージョンの関数が自動的に生成されます。これにより、私たちは一つの「設計図」を書くだけで、様々な型に対応できるのです。

クラステンプレート: 様々な型のデータを格納できるクラスを作る

テンプレートの力は、クラスにも適用できます。これにより、様々な型のデータを格納できる汎用的なクラス(コンテナなど)を作成できます。例えば、「2つの値をペアで保持する」クラスを考えてみましょう。

ファイルを編集:class_template_intro.cpp
#include <iostream>
#include <string>

// 2つの型 T1, T2 を引数に取るクラステンプレート
template <typename T1, typename T2>
class Pair {
public:
    T1 first;
    T2 second;

    // コンストラクタ
    Pair(T1 f, T2 s) : first(f), second(s) {}

    void print() {
        std::cout << "(" << first << ", " << second << ")" << std::endl;
    }
};

int main() {
    // T1=int, T2=std::string としてPairクラスのオブジェクトを生成
    Pair<int, std::string> p1(1, "apple");
    p1.print();

    // T1=std::string, T2=double としてPairクラスのオブジェクトを生成
    Pair<std::string, double> p2("pi", 3.14159);
    p2.print();
    
    // 違う型のPair同士は当然、別の型として扱われる
    // p1 = p2; // これはコンパイルエラーになる

    return 0;
}
ブラウザ上で動作するcppの実行環境です。
左上の実行ボタンを押して、このページ内のclass_template_intro.cppに書かれている内容を実行します。
(1, apple)
(pi, 3.14159)

クラステンプレートの仕組み

関数テンプレートと基本的な考え方は同じですが、いくつか重要な違いがあります。

  1. 明示的な型指定: 関数テンプレートではコンパイラが型を推論してくれましたが、クラステンプレートの場合は、オブジェクトを生成する際にPair<int, std::string>のように、開発者が明示的に型を指定する必要があります。

  2. インスタンス化: Pair<int, std::string>のように具体的な型を指定してオブジェクトを作ることを、テンプレートのインスタンス化と呼びます。コンパイラは、この指定に基づいてT1をintに、T2をstd::stringに置き換えた、以下のような新しいクラスを内部的に生成します。

    // コンパイラが内部的に生成するクラスのイメージ
    class Pair_int_string { // クラス名は実際には異なります
    public:
        int first;
        std::string second;
    
        Pair_int_string(int f, std::string s) : first(f), second(s) {}
    
        void print() {
            std::cout << "(" << first << ", " << second << ")" << std::endl;
        }
    };

    Pair<int, std::string>とPair<std::string, double>は、コンパイルされると全く別のクラスとして扱われることに注意してください。

クラステンプレートは、C++の強力なライブラリであるSTL (Standard Template Library)の根幹をなす技術です。第10章で学ぶvectorやmapといった便利なコンテナは、すべてクラステンプレートで実装されています。

この章のまとめ

  • ジェネリックプログラミングは、特定の型に縛られない、汎用的なコードを書くための手法です。
  • テンプレートは、C++でジェネリックプログラミングを実現するための機能です。
  • 関数テンプレートを使うと、様々な型の引数に対して同じ処理を行う関数を定義できます。呼び出し時には、コンパイラがテンプレート引数推論によって型を自動的に決定します。
  • クラステンプレートを使うと、様々な型を扱える汎用的なクラスを定義できます。オブジェクトを生成する際には、< >内に具体的な型を明示的に指定してインスタンス化する必要があります。

テンプレートを使いこなすことで、コードの再利用性が劇的に向上し、より柔軟で堅牢なプログラムを記述できるようになります。

練習問題1: 汎用的なprint関数

任意の型の配列(ここではstd::vectorを使いましょう)を受け取り、その要素をすべて画面に出力する関数テンプレートprint_elementsを作成してください。

ファイルを編集:practice10_1.cpp
#include <iostream>
#include <vector>
#include <string>

// ここに関数テンプレート print_elements を実装してください


int main() {
    std::vector<int> v_int = {1, 2, 3, 4, 5};
    std::cout << "Integers: ";
    print_elements(v_int);

    std::vector<std::string> v_str = {"C++", "is", "powerful"};
    std::cout << "Strings: ";
    print_elements(v_str);

    return 0;
}
ブラウザ上で動作するcppの実行環境です。
左上の実行ボタンを押して、このページ内のpractice10_1.cppに書かれている内容を実行します。
Integers: 1 2 3 4 5 
Strings: C++ is powerful 

練習問題2: 汎用的なスタッククラス

後入れ先出し(LIFO)のデータ構造であるスタックを、クラステンプレートSimpleStackとして実装してください。以下のメンバ関数を持つようにしてください。

  • void push(T item): スタックに要素を追加する
  • T pop(): スタックの先頭から要素を取り出す
  • bool is_empty(): スタックが空かどうかを返す

std::vectorを内部のデータ格納場所として利用して構いません。int型とchar型で動作を確認してください。

ファイルを編集:practice10_2.cpp
#include <iostream>
#include <vector>
#include <stdexcept>

// ここにクラステンプレート SimpleStack を実装してください

int main() {
    SimpleStack<int> int_stack;
    int_stack.push(10);
    int_stack.push(20);
    std::cout << "Popped from int_stack: " << int_stack.pop() << std::endl; // 20
    std::cout << "Popped from int_stack: " << int_stack.pop() << std::endl; // 10

    SimpleStack<char> char_stack;
    char_stack.push('A');
    char_stack.push('B');
    std::cout << "Popped from char_stack: " << char_stack.pop() << std::endl; // B
    std::cout << "Popped from char_stack: " << char_stack.pop() << std::endl; // A

    return 0;
}
ブラウザ上で動作するcppの実行環境です。
左上の実行ボタンを押して、このページ内のpractice10_2.cppに書かれている内容を実行します。
Popped from int_stack: 20
Popped from int_stack: 10
Popped from char_stack: B
Popped from char_stack: A
前のページ« 継承とポリモーフィズム
次のページSTL ①:コンテナ »