基本概念

C++中的模板编程是一种强大且灵活的编程技术,允许编写通用代码。模板通过参数化数据类型或操作,使得程序可以处理不同类型的数据,而无需重复编写类似代码。这种技术广泛应用于泛型编程,例如C++标准模板库(STL)中的容器、迭代器和算法就是基于模板实现的。

函数模板允许编写适用于不同类型的通用函数。你可以用一个模板参数定义一个函数,而不必为每种类型重写函数。

函数模板

#include <iostream>
using namespace std;

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

int main() {
    cout << "Add integers: " << add(5, 3) << endl;      // 调用时推断 T 为 int
    cout << "Add doubles: " << add(2.5, 3.1) << endl;   // 调用时推断 T 为 double
    return 0;
}
  • template <typename T>:这里T是一个类型模板参数,表示这个函数适用于任何类型T。
  • 函数add可以接收任意相同类型的两个参数,并返回它们的和。编译器会根据调用时的参数类型推导出T。

类模板

#include <iostream>
using namespace std;

template <typename T>
class Box {
    T value;
public:
    Box(T val) : value(val) {}
    T getValue() { return value; }
};

int main() {
    Box<int> intBox(123);         // Box 类的 int 实例
    Box<string> strBox("Hello");  // Box 类的 string 实例

    cout << "Int Box: " << intBox.getValue() << endl;
    cout << "String Box: " << strBox.getValue() << endl;
    return 0;
}

模板特化

模板的一个强大功能是模板特化。模板特化允许针对某个特定类型进行特殊化处理,即可以为某个类型提供一个特殊的实现。

全特化

全特化是指对模板的特定类型进行完全特化处理。

#include <iostream>
using namespace std;

template <typename T>
class Box {
public:
    T value;
    Box(T val) : value(val) {}
    void print() { cout << "Value: " << value << endl; }
};

// 对 char* 类型进行特化
template <>
class Box<char*> {
public:
    char* value;
    Box(char* val) : value(val) {}
    void print() { cout << "String: " << value << endl; }
};

int main() {
    Box<int> intBox(123);
    Box<char*> strBox("Hello Template");

    intBox.print();
    strBox.print();  // 调用特化版本
    return 0;
}

Box<char*>是Box类的特化版本,针对char*类型的模板进行了不同的实现。这样可以在处理字符串时,提供与一般模板不同的行为。

偏特化

偏特化允许我们对某些模板参数进行特化,而不需要对所有模板参数进行特化。

#include <iostream>
using namespace std;

// 泛型类模板
template <typename T1, typename T2>
class Pair {
public:
    T1 first;
    T2 second;
    Pair(T1 f, T2 s) : first(f), second(s) {}
    void print() { cout << first << ", " << second << endl; }
};

// 偏特化: 当两个类型相同时,采用特化版本
template <typename T>
class Pair<T, T> {
public:
    T first;
    T second;
    Pair(T f, T s) : first(f), second(s) {}
    void print() { cout << "Same type pair: " << first << ", " << second << endl; }
};

int main() {
    Pair<int, double> p1(1, 2.5);
    Pair<int, int> p2(3, 4);  // 调用偏特化版本

    p1.print();
    p2.print();  // 调用偏特化版本
    return 0;
}

模板元编程

模板不仅仅用于定义数据结构和函数,还可以进行编译期的计算,称为模板元编程。模板元编程允许通过递归模板在编译期执行逻辑运算,减少运行时的开销。

编译期阶乘计算

#include <iostream>
using namespace std;

// 模板递归计算阶乘
template <int N>
struct Factorial {
    enum { value = N * Factorial<N - 1>::value };
};

// 基例:Factorial<0> 为 1
template <>
struct Factorial<0> {
    enum { value = 1 };
};

int main() {
    cout << "Factorial<5>::value = " << Factorial<5>::value << endl;
    return 0;
}

Factorial<N>通过模板递归计算阶乘,直到基例Factorial<0>终止递归。这一计算是在编译时完成的,因此在运行时没有任何额外的开销。

Factorial<5>::value会展开成5 * 4 * 3 * 2 * 1,并在编译时得出结果。

变参模板

变参模板允许传递任意数量的模板参数,使得模板更加灵活。

#include <iostream>
using namespace std;

// 变参模板,处理多个参数
template <typename T, typename... Args>
void print(T first, Args... args) {
    cout << first << " ";
    if constexpr(sizeof...(args) > 0) {
        print(args...);  // 递归调用
    }
}

int main() {
    print(1, 2.5, "Hello", 'c');  // 传递多个不同类型的参数
    return 0;
}

template <typename T, typename... Args>中的Args...表示可以接受多个模板参数。

print函数递归处理每个参数,直到参数耗尽为止。

SFINAE(Substitution Failure Is Not An Error)

SFINAE是C++模板元编程中非常重要的概念。当模板参数替换失败时,并不导致编译错误,而是选择其他可用的重载版本。这通常用于特性检测和选择性模板实例化。

示例

#include <iostream>
#include <type_traits>
using namespace std;

// 通用模板
template <typename T>
typename enable_if<is_integral<T>::value, T>::type
foo(T t) {
    cout << "Integer type: " << t << endl;
    return t;
}

// 重载版本,用于非整数类型
template <typename T>
typename enable_if<!is_integral<T>::value, T>::type
foo(T t) {
    cout << "Non-integer type: " << t << endl;
    return t;
}

int main() {
    foo(10);        // 调用整数版本
    foo(3.14);      // 调用非整数版本
    return 0;
}

enable_if结合is_integral用于条件编译。根据传递的类型是否为整数类型,编译器会选择不同的模板实例化版本。