模板与泛型

翻译整理自 cppreference - 模板。

1. 函数模板

template <typename T>
T add(T a, T b) {
    return a + b;
}

add<int>(1, 2);          // 3
add(1.0, 2.0);           // 推断 double
add(std::string("a"), std::string("b"));

2. 多类型参数

template <typename T, typename U>
auto sum(T a, U b) -> decltype(a + b) {     // C++11 后置返回
    return a + b;
}

// C++14 起可省略 trailing return
template <typename T, typename U>
auto sum(T a, U b) { return a + b; }

3. 非类型参数

template <typename T, size_t N>
struct Array {
    T data[N];
    constexpr size_t size() const { return N; }
};

Array<int, 10> a;

4. 类模板

template <typename T>
class Stack {
public:
    void push(const T& v) { data_.push_back(v); }
    T pop() {
        T v = data_.back();
        data_.pop_back();
        return v;
    }
private:
    std::vector<T> data_;
};

Stack<int> s;
s.push(1);

5. 模板特化

全特化

template <typename T>
struct TypeName { static const char* get() { return "unknown"; } };

template <>
struct TypeName<int> { static const char* get() { return "int"; } };

template <>
struct TypeName<double> { static const char* get() { return "double"; } };

TypeName<int>::get();     // "int"
TypeName<float>::get();   // "unknown"

偏特化

template <typename T>
struct Vector { /* general */ };

template <typename T>
struct Vector<T*> { /* pointer specialization */ };

6. 变长模板

template <typename... Args>
void log(Args... args) {
    (std::cout << ... << args) << '\n';      // C++17 fold
}

log(1, " ", 2.0, " ", "x");

7. 概念 Concepts(C++20)

约束模板参数:

#include <concepts>

template <typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;

template <Numeric T>
T add(T a, T b) { return a + b; }

// 简写
auto add2(Numeric auto a, Numeric auto b) { return a + b; }

要求检查:

template <typename T>
requires std::integral<T>
T gcd(T a, T b) { ... }

8. SFINAE 与 enable_if

C++11 旧式约束:

template <typename T,
          typename = std::enable_if_t<std::is_integral_v<T>>>
T half(T x) { return x / 2; }

C++20 起用 concepts 替代,更易读。

9. 模板元编程

编译期计算:

template <int N>
struct Factorial {
    static constexpr int value = N * Factorial<N - 1>::value;
};

template <>
struct Factorial<0> {
    static constexpr int value = 1;
};

Factorial<5>::value;      // 120

C++11+ 推荐 constexpr 替代递归模板:

constexpr int fact(int n) {
    return n <= 1 ? 1 : n * fact(n - 1);
}

10. 模板编译模型

  • 模板定义通常放在头文件中
  • 显式实例化 template class Stack<int>; 可用于隐藏实现
  • C++20 modules 可替代 #include,缓解编译速度问题

小结

C++ 模板是强大的元编程工具,C++20 的 concepts 让错误信息更友好。