一、基础概念
1.1 什么是模板
模板是 C++ 的一个特性,使得你可以编写与类型无关的代码。模板通过定义类型参数,使得同一段代码可以适用于多种数据类型。
二、函数模板
2.1 定义与语法
-
基本定义:
-
template <typename T>
T functionName(T arg) {
// 函数体
} -
示例代码:
-
#include <iostream>
using namespace std;// 定义一个函数模板用于加法
template <typename T>
T add(T a, T b) {
return a + b; // 返回两数之和
}int main() {
cout << add(5, 10) << endl; // T 被推导为 int
cout << add(5.5, 10.5) << endl; // T 被推导为 double
return 0;
}
2.2 使用场景与示例
2.2.1 数学运算
函数模板适合处理不同数值类型的加法、乘法等操作。
// 数学运算示例
template <typename T>
T multiply(T a, T b) {
return a * b; // 返回两数之积
}
int main() {
cout << multiply(3, 4) << endl; // 输出: 12
cout << multiply(2.5, 4.0) << endl; // 输出: 10.0
return 0;
}
2.2.2 排序算法
可以实现一个通用的排序函数,适用于任何可比较的类型。
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
// 排序函数模板
template <typename T>
void sortArray(vector<T>& arr) {
sort(arr.begin(), arr.end()); // 使用 STL 的 sort 函数
}
int main() {
vector<int> intArr = {3, 1, 4, 1, 5};
sortArray(intArr);
for (const auto& num : intArr) {
cout << num << " "; // 输出: 1 1 3 4 5
}
cout << endl;
vector<string> strArr = {"banana", "apple", "cherry"};
sortArray(strArr);
for (const auto& str : strArr) {
cout << str << " "; // 输出: apple banana cherry
}
return 0;
}
2.2.3 容器操作
自定义容器类以处理任意数据类型。
#include <iostream>
#include <vector>
using namespace std;
// 自定义容器类模板
template <typename T>
class MyContainer {
public:
void add(const T& item) {
items.push_back(item); // 添加元素
}
void display() const {
for (const auto& item : items) {
cout << item << " "; // 输出所有元素
}
cout << endl;
}
private:
vector<T> items; // 内部存储
};
int main() {
MyContainer<int> intContainer;
intContainer.add(1);
intContainer.add(2);
intContainer.display(); // 输出: 1 2
MyContainer<string> strContainer;
strContainer.add("Hello");
strContainer.add("World");
strContainer.display(); // 输出: Hello World
return 0;
}
2.3 错误处理
- 类型不匹配:
- // 如果传入不支持的类型,将导致编译错误
cout << add("Hello", "World"); // 错误:不支持字符串相加 - 未定义行为:
- template <typename T>
T divide(T a, T b) {
return a / b; // 如果 b 为 0,将导致运行时错误
}
2.4 特化
- 完全特化:
- template <>
double add<double>(double a, double b) {
// 针对 double 类型的特化实现
return a + b; // 可增加特定的逻辑
} - 偏特化:
- template <typename T>
class MyClass<T*> { // 针对指针类型的特化
public:
void print() {
cout << "This is a pointer type." << endl;
}
};
2.5 常见问题
- 模板参数不明确:
// 当存在多重重载时,编译器可能无法推断参数类型
template <typename T>
void func(T a, T b);
func(1, 2); // 如果同时存在 func(int, float) 会导致编译错误
复杂的错误信息:
- 编译器给出的模板错误信息常常难以理解,特别是当模板嵌套时。
三、类模板
3.1 定义与语法
- 基本定义:
- template <typename T>
class ClassName {
// 成员变量和成员函数
}; -
#include <iostream>
using namespace std;// 类模板定义,包含一个类型为 T 的成员变量
template <typename T>
class Box {
public:
Box(T val) : value(val) {} // 构造函数
T getValue() const { return value; } // 返回值的成员函数private:
T value; // 成员变量
};int main() {
Box<int> intBox(123); // 使用 int 类型
Box<string> strBox("Hello"); // 使用 string 类型cout << intBox.getValue() << endl; // 输出: 123
cout << strBox.getValue() << endl; // 输出: Hello
return 0;
}
3.2 使用场景与示例
3.2.1 自定义数据结构
适合创建可以处理不同类型的自定义数据结构,如栈、队列。
#include <iostream>
#include <stack>
using namespace std;
// 自定义栈类模板
template <typename T>
class MyStack {
public:
void push(const T& item) {
s.push(item);
}
void pop() {
if (!s.empty()) {
s.pop();
}
}
T top() const {
return s.top();
}
private:
stack<T> s; // 内部使用标准栈
};
int main() {
MyStack<int> intStack;
intStack.push(1);
intStack.push(2);
cout << intStack.top() << endl; // 输出: 2
intStack.pop();
MyStack<string> strStack;
strStack.push("Hello");
strStack.push("World");
cout << strStack.top() << endl; // 输出: World
return 0;
}
3.3 错误处理
- 类型不匹配:
- // 使用不支持的类型将导致编译错误
MyStack<double> doubleStack;
doubleStack.push("Hello"); // 错误:类型不匹配
3.4 特化
- 类特化:
- template <>
class Box<int> { // 针对 int 类型的特化
public:
void print() {
cout << "This is an integer box." << endl;
}
};
3.5 常见问题
- 友元声明
template <typename T>
class MyClass;
template <typename T>
class FriendClass {
friend class MyClass<T>; // 友元模板
};
2.继承问题:
template <typename T>
class Base {
// 基类模板
};
template <typename T>
class Derived : public Base<T> {
// 派生类模板
};
四、进阶知识
4.1 可变参数模板
- 支持接受不定数量的参数。
-
#include <iostream>
using namespace std;// 可变参数模板
template <typename... Args>
void print(Args... args) {
(cout << ... << args) << endl; // 使用折叠表达式输出参数
}int main() {
print(1, 2, 3.5, "Hello"); // 输出: 123.5Hello
return 0;
}
4.2 模板元编程
- 使用模板进行编译时计算。
-
#include <iostream>
using namespace std;// 计算阶乘的模板
template <int N>
struct Factorial {
static const int value = N * Factorial<N - 1>::value; // 递归计算
};template <>
struct Factorial<0> { // 基础情况
static const int value = 1;
};int main() {
cout << "Factorial of 5: " << Factorial<5>::value << endl; // 输出: 120
return 0;
}
4.3 C++20 新特性:概念
- 用于约束模板参数类型,提高代码可读性和安全性
-
#include <iostream>
#include <concepts>
using namespace std;// 定义一个概念,要求类型 T 必须支持加法
template <typename T>
concept Addable = requires(T a, T b) {
{ a + b };
};// 使用概念限制模板参数
template <Addable T>
T add(T a, T b) {
return a + b; // 只允许支持加法的类型
}int main() {
cout << add(5, 10) << endl; // 输出: 15
return 0;
}
-
避免过度特化:
- 过度使用特化会使代码更复杂,增加维护难度。
- 示例:// 不建议为每个类型都特化
template <>
class Box<double> {
// 不必要的特化
};
-
合理使用 和 :使用 可以根据表达式自动推导类型。
template <typename T>
void func(T arg) {
decltype(arg) newArg = arg; // 根据 arg 类型推导 newArg
}
使用类型特征:
- 在模板中使用类型特征可以增加代码的类型安全性。
- 示例:#include <type_traits>
template <typename T>
void func(T arg) {
static_assert(std::is_integral<T>::value, "Type must be integral"); // 仅支持整型
} -
简化模板参数:
- 复杂的模板参数会导致代码可读性下降,尽量使用简单的类型或自定义类型。
-
调试复杂模板:
- 使用 捕捉错误。
- 示例:template <typename T>
class MyClass {
public:
static_assert(std::is_integral<T>::value, "T must be an integral type"); // 仅支持整型
};
特化与偏特化
1. 特化(Full Specialization)
定义: 特化是指为特定类型提供完全不同的实现。当你为一个特定类型定义一个模板的特殊版本时,称为特化。
理解: 假设你有一个模板函数或类,它可以处理多种类型,但你希望为特定的类型(如 或 )提供特定的实现,以优化性能或改变逻辑。
#include <iostream>
using namespace std;
// 基本函数模板
template <typename T>
T add(T a, T b) {
return a + b; // 通用实现
}
// 完全特化:针对 int 类型的特化
template <>
int add<int>(int a, int b) {
cout << "Using specialized add for int" << endl;
return a + b; // 特化实现
}
int main() {
cout << add(5, 10) << endl; // 使用特化,输出: Using specialized add for int
cout << add(2.5, 3.5) << endl; // 使用通用实现,输出: 6
return 0;
}
2. 偏特化(Partial Specialization)
定义: 偏特化是指对模板的部分参数进行特化,而不必提供所有参数的具体类型。它通常用于模板类。
理解: 偏特化可以让你为一组类型提供特殊的处理,而不是针对单一类型。这种特化可以帮助你处理更具体的情况。
示例:
#include <iostream>
using namespace std;
// 基本类模板
template <typename T>
class MyClass {
public:
void show() {
cout << "Generic MyClass" << endl;
}
};
// 偏特化:当类型为指针时
template <typename T>
class MyClass<T*> {
public:
void show() {
cout << "MyClass specialized for pointers" << endl;
}
};
int main() {
MyClass<int> obj1; // 使用通用实现
obj1.show(); // 输出: Generic MyClass
MyClass<int*> obj2; // 使用偏特化
obj2.show(); // 输出: MyClass specialized for pointers