11-模板编程

模板是C++泛型编程的基础,实现类型无关的代码。模板在编译期展开,零运行时开销但增加编译时间。

函数模板

函数模板定义通用算法,适用于多种类型。编译器根据实参自动推导模板参数类型。

基本语法

函数模板用 template<typename T> 声明。T 是类型参数,可用于参数类型、返回类型、局部变量类型。

// 函数模板定义
template<typename T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

// 使用
int result1 = maximum(10, 20);           // T推导为int
double result2 = maximum(3.14, 2.71);    // T推导为double
string result3 = maximum(string("hello"), string("world")); // T推导为string

多参数模板

模板可以有多个类型参数或非类型参数。不同类型参数间可以互相操作,结果类型用 decltypeauto 推导。

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

// 使用
auto result = add(10, 3.14);  // 返回double

模板特化

模板特化为特定类型提供定制实现。全特化指定所有模板参数,偏特化指定部分参数。特化优先于通用模板。

// 主模板
template<typename T>
void print(T value) {
    cout << "Generic: " << value << endl;
}

// 特化版本
template<>
void print<string>(string value) {
    cout << "String: " << value << endl;
}

// 使用
print(42);           // 调用主模板
print(string("hi")); // 调用特化版本

非类型模板参数

template<int N>
class Array {
private:
    int data[N];
public:
    int& operator[](int index) {
        return data[index];
    }
    int size() const { return N; }
};

// 使用
Array<10> arr;  // 编译时确定大小

类模板

类模板定义通用数据结构。STL容器都是类模板,如 vector<T>map<K,V>。类模板需要显式指定模板参数。

基本类模板

类模板在类名后用 <T> 声明模板参数。成员函数定义需要重复模板声明。模板定义通常放在头文件中。

template<typename T>
class Stack {
private:
    vector<T> elements;
    
public:
    void push(const T& element) {
        elements.push_back(element);
    }
    
    void pop() {
        if (!elements.empty()) {
            elements.pop_back();
        }
    }
    
    T& top() {
        return elements.back();
    }
    
    bool empty() const {
        return elements.empty();
    }
    
    size_t size() const {
        return elements.size();
    }
};

// 使用
Stack<int> intStack;
Stack<string> stringStack;

模板成员函数

class Container {
public:
    template<typename T>
    void process(T value) {
        cout << "Processing: " << value << endl;
    }
    
    template<typename T, typename U>
    void combine(T a, U b) {
        cout << "Combined: " << a << " + " << b << endl;
    }
};

// 使用
Container c;
c.process(42);
c.process(string("hello"));
c.combine(10, 3.14);

类模板特化

// 主模板
template<typename T>
class Vector {
private:
    T* data;
    size_t size;
public:
    Vector(size_t s) : size(s), data(new T[s]) {}
    ~Vector() { delete[] data; }
};

// 特化版本
template<>
class Vector<bool> {
private:
    unsigned char* data;
    size_t size;
public:
    Vector(size_t s) : size(s), data(new unsigned char[(s + 7) / 8]) {}
    ~Vector() { delete[] data; }
    
    bool operator[](size_t index) const {
        return (data[index / 8] >> (index % 8)) & 1;
    }
    
    void set(size_t index, bool value) {
        if (value) {
            data[index / 8] |= (1 << (index % 8));
        } else {
            data[index / 8] &= ~(1 << (index % 8));
        }
    }
};

模板元编程

编译时计算

// 编译时阶乘
template<int N>
struct Factorial {
    static const int value = N * Factorial<N - 1>::value;
};

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

// 使用
const int fact5 = Factorial<5>::value;  // 编译时计算为120

SFINAE(替换失败不是错误)

#include <type_traits>

// 检查类型是否有size()成员函数
template<typename T>
class has_size {
private:
    template<typename U>
    static auto test(int) -> decltype(std::declval<U>().size(), std::true_type{});
    
    template<typename>
    static std::false_type test(...);
    
public:
    static const bool value = decltype(test<T>(0))::value;
};

// 使用
cout << has_size<vector<int>>::value << endl;  // true
cout << has_size<int>::value << endl;          // false

类型萃取

#include <type_traits>

// 移除引用
template<typename T>
using remove_reference_t = typename std::remove_reference<T>::type;

// 条件类型
template<bool B, typename T, typename F>
using conditional_t = typename std::conditional<B, T, F>::type;

// 使用
remove_reference_t<int&> a = 42;  // a的类型是int
conditional_t<true, int, double> b = 10;  // b的类型是int

变参模板(C++11)

基本语法

// 变参函数模板
template<typename... Args>
void print(Args... args) {
    ((cout << args << " "), ...);  // C++17折叠表达式
    cout << endl;
}

// 使用
print(1, 2.5, "hello", 'c');  // 输出: 1 2.5 hello c

变参类模板

template<typename... Types>
class Tuple;

// 特化:空元组
template<>
class Tuple<> {};

// 特化:非空元组
template<typename Head, typename... Tail>
class Tuple<Head, Tail...> {
private:
    Head head;
    Tuple<Tail...> tail;
    
public:
    Tuple(Head h, Tail... t) : head(h), tail(t...) {}
    
    Head& getHead() { return head; }
    Tuple<Tail...>& getTail() { return tail; }
};

完美转发

template<typename T>
void forwardValue(T&& value) {
    process(std::forward<T>(value));
}

template<typename T>
void process(T&& value) {
    cout << "Processing: " << value << endl;
}

// 使用
int x = 42;
forwardValue(x);        // 左值
forwardValue(42);       // 右值
forwardValue(std::move(x)); // 右值

概念(C++20)

基本概念

#include <concepts>

// 定义概念
template<typename T>
concept Addable = requires(T a, T b) {
    a + b;  // 必须支持加法
};

template<typename T>
concept Printable = requires(T t) {
    std::cout << t;  // 必须可打印
};

// 使用概念
template<Addable T>
T add(T a, T b) {
    return a + b;
}

template<Printable T>
void print(T value) {
    std::cout << value << std::endl;
}

约束模板

#include <concepts>

// 约束函数模板
template<std::integral T>
T multiply(T a, T b) {
    return a * b;
}

// 约束类模板
template<std::copyable T>
class Container {
private:
    T value;
public:
    Container(const T& v) : value(v) {}
    T get() const { return value; }
};

// 使用
auto result = multiply(5, 3);        // OK
// auto result2 = multiply(5.0, 3.0); // 错误:不满足integral约束

模板实例化

显式实例化

// 头文件中的模板定义
template<typename T>
void process(T value) {
    cout << "Processing: " << value << endl;
}

// 源文件中的显式实例化
template void process<int>(int);
template void process<double>(double);
template void process<string>(string);

实例化控制

// 防止隐式实例化
extern template void process<int>(int);

// 显式实例化声明
template void process<double>(double);

模板调试

编译错误诊断

// 使用static_assert进行编译时检查
template<typename T>
void process(T value) {
    static_assert(std::is_arithmetic_v<T>, "T must be arithmetic type");
    cout << "Processing: " << value << endl;
}

模板元编程调试

// 使用typeid获取类型信息
template<typename T>
void debugType() {
    cout << "Type: " << typeid(T).name() << endl;
}

// 使用decltype进行类型推导
template<typename T>
void debugDecltype(T value) {
    using type = decltype(value);
    cout << "Decltype: " << typeid(type).name() << endl;
}

SFINAE 与编译期编程

SFINAE(替换失败不是错误)

SFINAE是C++模板的强大特性:模板参数替换失败时不报错,而是尝试其他重载。

#include <type_traits>

// 示例1:根据类型特性启用不同函数
// 只对整数类型启用
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
processInteger(T value) {
    return value * 2;
}

// 只对浮点类型启用
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
processFloat(T value) {
    return value * 1.5;
}

// 使用
int x = processInteger(10);      // OK,T=int
double y = processFloat(3.14);   // OK,T=double
// processInteger(3.14);          // 编译错误,没有匹配的重载

// C++17简化写法:std::enable_if_t
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
processInteger(T value) {
    return value * 2;
}

if constexpr(C++17)

编译期条件判断,未选中分支不编译,比SFINAE更清晰。

// C++17之前:需要函数重载
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
process(T value) {
    return value * 2;
}

template<typename T>
std::enable_if_t<std::is_floating_point_v<T>, T>
process(T value) {
    return value * 1.5;
}

// C++17:if constexpr
template<typename T>
T process(T value) {
    if constexpr (std::is_integral_v<T>) {
        return value * 2;
    } else if constexpr (std::is_floating_point_v<T>) {
        return value * 1.5;
    } else {
        return value;
    }
}

编译期计算

模板可在编译期执行复杂计算,零运行时开销。

// 编译期计算阶乘
template<int N>
struct Factorial {
    static constexpr int value = N * Factorial<N - 1>::value;
};

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

// 使用
constexpr int result = Factorial<5>::value;  // 编译期计算得120

// C++14:constexpr函数(更简洁)
constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

constexpr int result2 = factorial(5);  // 编译期计算

// 编译期检查数组大小
template<size_t N>
constexpr bool isPowerOfTwo() {
    return N > 0 && (N & (N - 1)) == 0;
}

static_assert(isPowerOfTwo<8>(), "Size must be power of 2");
// static_assert(isPowerOfTwo<7>(), "");  // 编译错误!

Concepts(C++20)

取代SFINAE,提供更清晰的约束表达。

#include <concepts>

// 定义concept
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;

// 使用concept约束模板
template<Numeric T>  // 只接受数值类型
T add(T a, T b) {
    return a + b;
}

// 更复杂的concept
template<typename T>
concept Container = requires(T t) {
    t.begin();
    t.end();
    t.size();
};

template<Container C>
void printContainer(const C& container) {
    for (const auto& item : container) {
        std::cout << item << " ";
    }
}

// concept比SFINAE的优势:
// 1. 错误信息更清晰
// 2. 语法更简洁
// 3. 可以组合和复用

模板编程最佳实践

// ✅ 1. 优先使用concept(C++20)或enable_if约束模板
template<typename T>
requires std::is_integral_v<T>  // C++20
T square(T x) { return x * x; }

// ✅ 2. 使用if constexpr简化代码
template<typename T>
void print(T value) {
    if constexpr (std::is_pointer_v<T>) {
        std::cout << "Pointer: " << *value;
    } else {
        std::cout << "Value: " << value;
    }
}

// ✅ 3. 模板定义放头文件(或用extern template)
// header.h
template<typename T>
class MyClass { /* ... */ };

// ✅ 4. 用constexpr代替模板元编程
constexpr int factorial(int n) {  // 简单清晰
    return n <= 1 ? 1 : n * factorial(n - 1);
}

// ❌ 复杂的模板元编程(难读)
template<int N>
struct Fact { static const int value = N * Fact<N-1>::value; };

原则:模板是工具而非目的,优先可读性和简洁性。