C++におけるテンプレートとジェネリックプログラミング

テンプレートの基本概念

テンプレートはクラスや関数の設計図であり、コンパイル時に特定の型に基づいて具体的なコードが生成される。このプロセスをインスタンス化と呼ぶ。

関数テンプレートの定義

// 型パラメータTを持つ関数テンプレート
template<typename T>
int compare_values(const T& lhs, const T& rhs) {
    if (lhs < rhs) return -1;
    if (rhs < lhs) return 1;
    return 0;
}

非型テンプレートパラメータ

// 文字列リテラルの長さをテンプレートパラメータとして受け取る
template<size_t N, size_t M>
int compare_strings(const char (&s1)[N], const char (&s2)[M]) {
    return std::strcmp(s1, s2);
}

// 使用例: 配列サイズが自動的に推論される
compare_strings("hello", "world"); // N=6, M=6(ヌル終端含む)

クラステンプレートの実装

template<typename T>
class Container {
public:
    using value_type = T;
    using size_type = typename std::vector<T>::size_type;

    Container() : data(std::make_shared<std::vector<T>>()) {}
    Container(std::initializer_list<T> init) 
        : data(std::make_shared<std::vector<T>>(init)) {}

    void add(const T& item) { data->push_back(item); }
    T& get(size_type idx) { 
        if (idx >= data->size()) 
            throw std::out_of_range("Index out of bounds");
        return (*data)[idx]; 
    }

private:
    std::shared_ptr<std::vector<T>> data;
};

クラステンプレートのメンバ関数定義

template<typename T>
T& Container<T>::get(typename Container<T>::size_type idx) {
    // 実装内容...
}

テンプレート型エイリアス

// ペア型のエイリアス
template<typename T> 
using Pair = std::pair<T, T>;

Pair<std::string> authors; // std::pair<std::string, std::string>

// 部分固定パラメータ
template<typename T> 
using IdPair = std::pair<T, unsigned int>;

IdPair<Product> product_entry; // std::pair<Product, unsigned int>

テンプレート引数の推論と制御

明示的テンプレート引数指定

template<typename ReturnType, typename T1, typename T2>
ReturnType calculate(T1 a, T2 b) {
    return static_cast<ReturnType>(a + b);
}

// 使用例
auto result = calculate<long>(3, 4.5); // long型で結果を返す

右辺値参照とフォワーディング

// 完全転送を実現するヘルパー関数
template<typename T>
typename std::remove_reference<T>::type&& move_value(T&& arg) {
    return static_cast<typename std::remove_reference<T>::type&&>(arg);
}

// 可変引数テンプレートでの転送
template<typename Func, typename... Args>
void execute(Func&& f, Args&&... args) {
    f(std::forward<Args>(args)...);
}

可変引数テンプレート

再帰的処理パターン

// 基本ケース:単一要素の処理
template<typename T>
void log_args(const T& value) {
    std::cout << value << std::endl;
}

// 再帰ケース:先頭要素を処理後、残りを再帰呼び出し
template<typename T, typename... Rest>
void log_args(const T& first, const Rest&... rest) {
    std::cout << first << ", ";
    log_args(rest...);
}

// 使用例
log_args(1, "hello", 3.14); // 出力: 1, hello, 3.14

パラメータパックの展開

template<typename... Args>
void process_all(Args&&... args) {
    // 各引数にdebug_info関数を適用
    auto dummy = {(debug_info(std::forward<Args>(args)), 0)...};
    (void)dummy; // 警告抑制
}

テンプレートの特殊化

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

// 一般テンプレート
template<typename T>
int compare(const T& a, const T& b) { /* 一般実装 */ }

// 文字列ポインタ用の特殊化
template<>
int compare<const char*>(const char* const& a, const char* const& b) {
    return std::strcmp(a, b);
}

クラステンプレートの部分特殊化

// 一般テンプレート
template<typename T>
struct TypeWrapper {
    using type = T;
};

// ポインタ型用の部分特殊化
template<typename T>
struct TypeWrapper<T*> {
    using type = T;
    static constexpr bool is_pointer = true;
};

// 使用例
TypeWrapper<int*>::is_pointer; // true

標準ライブラリの特殊化例

namespace std {
template<>
struct hash<CustomType> {
    size_t operator()(const CustomType& obj) const {
        return hash<string>()(obj.id) ^ 
               hash<int>()(obj.value);
    }
};
}

タグ: C++ テンプレート ジェネリックプログラミング 可変引数テンプレート テンプレート特殊化

6月22日 18:10 投稿