C++中的标准模板库(STL)
C++标准模板库(STL),这是一组内建库,提供了一组常用的数据结构(如 vector、list、stack、queue、map 等)和算法,可提升开发效率与性能。
C++ 模板(Templates)
C++ 模板是创建泛型类或函数的利器,让我们只需编写一次代码,就能适用于任意数据类型,无需为每种类型重复劳动。
模板的核心卖点:
- 消灭重复代码:一份函数/类,通吃所有类型,真正“写一次,跑所有”。
- 类型安全:比
void*或宏更靠谱,编译器帮你把关类型。 - 可特化:特殊类型(如
vector<bool>)可以单独开小灶,定制专属实现。 - STL 的基石:
vector、map、sort等容器和算法,全靠模板撑腰。
#include <iostream>
using namespace std;
// Template function 模板方法,两个vlaue比较,返回最小的value
template <typename T> T myMax(T x, T y){
return (x > y) ? x : y;
}
int main(){
// Using myMax
cout << "Max of 3 and 7 is: " << myMax<int>(3, 7) << endl;
cout << "Max of 3.5 and 7.5 is :" << myMax<double>(3.5, 7.5) << endl;
cout << "Max of 'g' and 'e' is: " << myMax<char>('g', 'e') << endl;
return 0;
}
Max of 3 and 7 is: 7
Max of 3.5 and 7.5 is :7.5
Max of 'g' and 'e' is: g
1.模板定义
模板用关键字 template 和 typename 来定义,格式如下:
template <typename A, typename B, ...> entity_definition
template告诉编译器:接下来是一个模板。typename用来声明模板参数,这些参数代表“将来才给”的具体类型。- 任何时候都可以用关键字
class替换typename,效果一样。
上面这套语法可以为 C++ 中的三种“实体”定义模板:
- 函数模板
- 类模板
- 变量模板(C++14 起支持)
2.函数模板
在 C++ 里,模板让我们写出一份函数源码,就能自动适配不同数据类型,这就是函数模板。
例如:写一个取两数较大值的函数,无论传 int、float 还是 double 都能直接用。
示例代码(已附中文注释):
#include <iostream>
using namespace std;
// 函数模板定义
template <typename T> T myMax(T x, T y){
return (x > y) ? x : y;
}
int main(){
cout << myMax<int>(3, 7) << endl; // 用 int 实例化
cout << myMax<double>(3.0, 7.0) << endl; // 用 double 实例化
cout << myMax<char>('g', 'e'); // 用 char 实例化
return 0;
}
7
7
g
3.类模板
与函数模板类似,当类的逻辑与具体数据类型无关时,就用类模板。典型例子:链表、二叉树、栈、队列、数组等容器。
单类型示例 :
#include <iostream>
using namespace std;
// 类模板定义
template <typename T> class Geek{
public:
T x;
T y;
// 构造函数
Geek(T val1, T val2) : x(val1), y(val2) {}
// 打印成员
void getValues(){
cout << x << " " << y;
}
};
int main(){
Geek<int> intGeek(10, 20); // 用 int 实例化
Geek<double> doubleGeek(3.14, 6.28); // 用 double 实例化
intGeek.getValues(); // 输出:10 20
cout << endl;
doubleGeek.getValues(); // 输出:3.14 6.28
return 0;
}
多类型示例 :
模板也可以一次接受多个类型参数:
template <typename A, typename B, typename C> class Geek{
public:
A x;
B y;
C z;
Geek(A val1, B val2, C val3) : x(val1), y(val2), z(val3) {
}
void getValues(){
cout << x << " " << y << " " << z;
}
};
// 使用
Geek<int, double, string> obj1(10, 3.14, "Hello");
Geek<char, float, bool> obj2('A', 5.67f, true);
obj1.getValues(); // 10 3.14 Hello
obj2.getValues(); // A 5.67 1
4.模板变量(C++14 起)
模板变量 = “变量也能泛型”。
像函数模板、类模板一样,只要在使用时给出类型,就能生成对应版本的常量。
语法
template <typename T> constexpr T pi = T(3.14159);
pi就是模板变量。constexpr保证它是编译期常量,而非运行期计算。
示例代码:
#include <iostream>
using namespace std;
template <typename T> constexpr T pi = T(3.14159);
int main()
{
cout << "Pi as float: " << pi<float> << endl; // 3.14159
cout << "Pi as double: " << pi<double> << endl; // 3.14159
return 0;
}
Pi as float: 3.14159
Pi as double: 3.14159
5.默认模板实参
和普通函数参数一样,模板也能给类型参数指定“默认值”。
如果实例化时没写对应类型,编译器就自动用默认值补上。
示例代码:
#include <iostream>
using namespace std;
// 类模板,给 T2、T3 设了默认类型
template <typename T1, typename T2=double, typename T3=string> class Geek{
public:
T1 x;
T2 y;
T3 z;
Geek(T1 val1, T2 val2, T3 val3) : x(val1), y(val2), z(val3) {}
void getValues(){
cout << x << " " << y << " " << z;
}
};
int main(){
// 全显式指定
Geek<int, float, string> intFloatStringGeek(10, 5.67f, "Hello");
// 只给 T1,后两个用默认的 double 和 string
Geek<char> charDoubleStringGeek('A', 3.14, "World");
intFloatStringGeek.getValues(); // 10 5.67 Hello
cout << endl;
charDoubleStringGeek.getValues(); // A 3.14 World
return 0;
}
10 5.67 Hello
A 3.14 World
6.非类型模板参数
模板不光能传“类型”,还能传“值”。
这些非类型参数必须是编译期常量(const / constexpr),因为编译器要在编译阶段就生成对应的函数或类。
示例代码:
#include <iostream>
using namespace std;
// T 是类型,max 是整型常量(非类型参数)
template <class T, int max> int arrMin(T arr[], int n){
int m = max; // 先把假设的最小值设成 max
for (int i = 0; i < n; ++i){
if (arr[i] < m){
m = arr[i];
}
}
return m;
}
int main(){
int arr1[] = {10, 20, 15, 12};
char arr2[] = {1, 2, 3};
// 10000 和 256 都是编译期常量,作为非类型实参传入
cout << arrMin<int, 10000>(arr1, 4) << endl; // 输出 10
cout << arrMin<char, 256> (arr2, 3) << endl; // 输出 1
return 0;
}
10
1
7.模板实参推导(Template Argument Deduction)
编译器能够自动推断传给模板的实参类型,我们就不必手写类型列表。
注意:
- 函数模板一直支持推导。
- 类模板的自动推导要到 C++17 才引入;在 C++14 及以前写
ClassName{args}而不显式给类型会编译失败。
示例:STL 的 max() 自动推导 (若在 C++14 或更低版本尝试对类模板使用同样写法,会报错。)
#include <bits/stdc++.h>
using namespace std;
int main()
{
cout << max(3, 4); // 编译器推导出 max<int>(3, 4)
return 0;
}
4
8.函数模板实参推导
从 C++98 起就支持:调用函数模板时不必写明类型,编译器会根据实参自动推导出模板参数。
示例:
#include <iostream>
using namespace std;
template <typename T> T multiply(T first, T second){
return first * second;
}
int main()
{
cout << multiply(3, 4); // 自动推导出 multiply<int>(3, 4)
return 0;
}
12
注意:
如果模板里所有参数都是同一个类型参数 T(如 template<typename T> void func(T a, T b)),
就不能混传不同类型(例如 func(3, 4.5) 会编译失败),否则推导会出现冲突。
9.类模板实参推导(C++17 起)
从 C++17 开始,类模板也能像函数模板一样自动推导模板参数,无需显式写出类型。
示例:
#include <iostream>
using namespace std;
template <typename T> class Geek {
public:
T x;
T y;
Geek(T val1, T val2) : x(val1), y(val2) {}
void getValues(){
cout << x << " " << y;
}
};
int main()
{
Geek intGeek(10, 20); // 自动推导出 Geek<int>
Geek doubleGeek(3.14, 6.28); // 自动推导出 Geek<double>
intGeek.getValues(); // 输出:10 20
cout << endl;
doubleGeek.getValues(); // 输出:3.14 6.28
return 0;
}
10 20
3.14 6.28