学而时习之:C++中的标准模板库1

62 阅读5分钟

C++中的标准模板库(STL)

C++标准模板库(STL),这是一组内建库,提供了一组常用的数据结构(如 vector、list、stack、queue、map 等)和算法,可提升开发效率与性能。

C++ 模板(Templates)

C++ 模板是创建泛型类或函数的利器,让我们只需编写一次代码,就能适用于任意数据类型,无需为每种类型重复劳动。

模板的核心卖点:

  • 消灭重复代码:一份函数/类,通吃所有类型,真正“写一次,跑所有”。
  • 类型安全:比 void*更靠谱,编译器帮你把关类型。
  • 可特化:特殊类型(如 vector<bool>)可以单独开小灶,定制专属实现。
  • STL 的基石vectormapsort 等容器和算法,全靠模板撑腰。
#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

image.png

1.模板定义

模板用关键字 templatetypename 来定义,格式如下:

template <typename A, typename B, ...> entity_definition
  • template 告诉编译器:接下来是一个模板。
  • typename 用来声明模板参数,这些参数代表“将来才给”的具体类型。
  • 任何时候都可以用关键字 class 替换 typename,效果一样。

上面这套语法可以为 C++ 中的三种“实体”定义模板:

  • 函数模板
  • 类模板
  • 变量模板(C++14 起支持)

2.函数模板

在 C++ 里,模板让我们写出一份函数源码,就能自动适配不同数据类型,这就是函数模板。
例如:写一个取两数较大值的函数,无论传 intfloat 还是 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