C++ 从入门到精通之二十

大纲

C++ 进阶

可变参数函数与初始化列表

省略号形参

核心概念

C++ 省略号形参(Ellipsis Parameter,写作 ...)是一种允许函数接受任意数量、任意类型额外实参的机制;它只能出现在形参列表末尾,且其前面的形参必须至少有一个具名参数以便定位省略号部分的起点。由于省略号形参完全绕过类型检查,编译器无法得知传入实参的个数与类型,因此调用方与函数实现方必须自行约定传递规则,并在运行时通过 va_list、va_start、va_arg、va_end 等宏(定义在 <stdarg.h> 头文件中)手动遍历和读取这些实参;这也带来类型不匹配、整型提升(如 float 提升为 double、小整型提升为 int)以及非平凡类型(如带构造 / 析构的类对象)不能安全传递等限制,属于类型不安全、易出错且难以维护的过时特性。现代 C++ 通常以可变参模板、std::initializer_list 或参数包展开等类型安全的方式替代它,省略号形参主要保留在 printf 系列函数及与 C 语言兼容的接口中。

使用案例
使用案例一
  • 省略号形参的基本用法:自定义求和函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <cstdarg>
#include <cstdio>

// 第一个参数 count 指明后面省略号部分实参的个数
int sum(int count, ...) {
va_list args;
va_start(args, count); // 以最后一个具名形参为起点
int total = 0;
for (int i = 0; i < count; ++i) {
total += va_arg(args, int); // 每次按 int 类型读取一个实参
}
va_end(args); // 清理
return total;
}

int main() {
std::printf("%d\n", sum(3, 10, 20, 30));
std::printf("%d\n", sum(5, 1, 2, 3, 4, 5));
}

程序运行的结果如下:

1
2
60
15
使用案例二
  • 使用省略号形参模拟 printf:以格式串驱动类型读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <string>

void my_print(const char* fmt, ...) {
va_list args;
va_start(args, fmt);

for (const char* p = fmt; *p; ++p) {
if (*p != '%') {
std::putchar(*p);
continue;
}

// 遇到 '%',收集完整格式说明(到转换字符为止)
std::string spec = "%";
const char* q = p + 1;

if (*q == '%') { // "%%" 输出一个百分号
std::putchar('%');
p = q;
continue;
}

// 收集标志、宽度、精度
while (*q && std::strchr("-+ #0", *q)) {
spec += *q++;
}

// 收集宽度
while (*q && (*q >= '0' && *q <= '9')) {
spec += *q++;
}

// 收集精度
if (*q == '.') {
spec += *q++;

while (*q && (*q >= '0' && *q <= '9')) {
spec += *q++;
}
}

// 收集长度修饰符
std::string length;
if (*q && std::strchr("hlLjzt", *q)) {
length += *q;
spec += *q++;

// 处理 hh、ll
if ((length[0] == 'h' || length[0] == 'l') && *q == length[0]) {
length += *q;
spec += *q++;
}
}

if (!*q) { // 格式串在 '%' 后意外结束
std::putchar('%');
break;
}

char conv = *q; // 真正的转换字符
spec += conv;
p = q; // 让外层循环从转换字符之后继续

switch (conv) {
case 'd':
case 'i':
case 'u':
case 'o':
case 'x':
case 'X':
// 整数类型:根据长度修饰符读取对应类型
if (length == "l") {
std::printf(spec.c_str(), va_arg(args, long));
} else if (length == "ll") {
std::printf(spec.c_str(), va_arg(args, long long));
} else if (length == "z") {
std::printf(spec.c_str(), va_arg(args, size_t));
} else if (length == "t") {
std::printf(spec.c_str(), va_arg(args, ptrdiff_t));
} else {
// hh、h 对应的实参通常经过整型提升
std::printf(spec.c_str(), va_arg(args, int));
}
break;

case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
case 'a':
case 'A':
// 浮点类型:float 经默认实参提升后为 double
if (length == "L") {
std::printf(spec.c_str(), va_arg(args, long double));
} else {
std::printf(spec.c_str(), va_arg(args, double));
}
break;

case 'c':
// char 经默认实参提升后为 int
std::printf(spec.c_str(), va_arg(args, int));
break;

case 's':
std::printf(spec.c_str(), va_arg(args, const char*));
break;

case 'p':
std::printf(spec.c_str(), va_arg(args, void*));
break;

default:
// 未支持的转换,原样输出,不读取额外参数
std::printf("%s", spec.c_str());
break;
}
}

va_end(args);
}

int main() {
my_print("name=%s age=%d score=%.1f\n", "Tom", 20, 95.5);
my_print("hex=%x unsigned=%u char=%c\n", 255, 100u, 'A');
my_print("long=%ld double=%.2f\n", 123456L, 3.14159);
my_print("percent=%% pointer=%p\n", static_cast<void*>(nullptr));
return 0;
}

程序运行的结果如下:

1
2
3
4
name=Tom age=20 score=95.5
hex=ff unsigned=100 char=A
long=123456 double=3.14
percent=% pointer=0000000000000000

可变参数函数

核心概念

C++ 可变参数函数是指参数数量(及类型)在编译期不固定的函数,主要分为两类实现方式:一是继承自 C 语言,通过省略号(...)配合 va_list / va_start / va_arg / va_end 等宏(定义在 <stdarg.h> 头文件中)访问参数的传统机制,它不做类型检查、依赖调用者自行约定参数类型与数量,安全性较弱;二是 C++ 11 引入的可变参模板(Variadic Templates),借助参数包与递归或折叠表达式在编译期展开,类型安全且支持完美转发,是现代 C++ 推荐的做法。

使用案例
C 实现方式
  • C 语言实现可变参数函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <cstdarg>
#include <cstdio>

// ==================================================================
// 实现方式一:第一个参数 count 指明后续可变参数的个数
// ==================================================================
int sum(int count, ...) {
va_list args; // 声明参数列表
va_start(args, count); // 初始化,last named param 为 count

int total = 0;
for (int i = 0; i < count; ++i) {
total += va_arg(args, int); // 每次取出一个 int
}

va_end(args); // 清理

return total;
}

// ==================================================================
// 实现方式二:用哨兵值(如 -1)标记结束的可变参数求和函数
// ==================================================================
int sum_until_neg(int first, ...) {
va_list args; // 声明参数列表
va_start(args, first); // 初始化,last named param 为 first

int total = first; // 先计入第一个具名参数
int v;
while ((v = va_arg(args, int)) != -1) { // 每次取出一个 int,遇 -1 结束
total += v;
}

va_end(args); // 清理
return total;
}

int main() {
std::printf("sum(3, 10, 20, 30) = %d\n", sum(3, 10, 20, 30));
std::printf("sum_until_neg(10, 20, 30, -1) = %d\n", sum_until_neg(10, 20, 30, -1));
return 0;
}

程序运行的结果如下:

1
2
sum(3, 10, 20, 30) = 60
sum_until_neg(10, 20, 30, -1) = 60
C++ 实现方式
  • C++ 实现可变参数函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <string>
#include <utility>

// ============================================================
// 实现方式一:递归展开(C++ 11 起支持)
// ============================================================

// 递归终止条件:只剩一个参数时直接返回
template <typename T>
T sum_recursive(T v) {
return v;
}

// 递归情形:取出第一个参数 head,对剩余参数包 tail... 递归求和
template <typename T, typename... Args>
T sum_recursive(T head, Args... tail) {
return head + sum_recursive(tail...); // tail... 在编译期展开为实参列表
}

// ============================================================
// 实现方式二:折叠表达式(C++ 17 起支持),无需递归终止函数
// ============================================================

// 一元右折叠:(args + ...) 展开为 a1 + (a2 + (a3 + ...))
template <typename... Args>
auto sum_fold(Args... args) {
return (args + ...);
}

// ============================================================
// 实现方式三:完美转发 —— 保持实参的值类别与类型
// ============================================================

// 递归终止:处理最后一个参数
template <typename T>
void print_all(T&& last) {
std::cout << std::forward<T>(last) << std::endl;
}

// 递归情形:转发当前参数,再递归处理剩余参数
// T&& 为转发引用,std::forward 保持左值/右值属性
template <typename T, typename... Args>
void print_all(T&& head, Args&&... tail) {
std::cout << std::forward<T>(head) << ' ';
print_all(std::forward<Args>(tail)...);
}

// ============================================================
// 补充:sizeof... 获取参数包中参数个数
// ============================================================
template <typename... Args>
constexpr std::size_t count_args(Args&&...) {
return sizeof...(Args); // 编译期得到参数个数
}

int main() {
std::cout << sum_recursive(1, 2, 3, 4, 5) << std::endl;
std::cout << sum_fold(1.5, 2.5, 3.0) << std::endl;
print_all(42, "hello", "world", 3.14);
std::cout << "arg count = " << count_args(1, 'a', 3.0) << std::endl;
return 0;
}

程序运行的结果如下:

1
2
3
4
15
7
42 hello world 3.14
arg count = 3

std::initializer_list

核心概念

std::initializer_list 是 C++ 11 引入的轻量级只读容器视图(定义于 <initializer_list> 头文件),用于在花括号初始化列表(如 {1, 2, 3})与函数参数、构造函数之间传递同类型元素序列;它内部仅持有指向常量元素数组的首指针和长度,不拥有元素、不可修改元素、不支持增删,拷贝代价极低(仅复制两个指针),且底层数组的生命周期与初始化列表表达式所在的作用域绑定,因此不能返回或长期保存其引用;它常用于构造函数(如容器类的 initializer_list 重载)和接受任意数量同类型实参的函数,但需注意其元素类型必须一致、空列表 {} 的推导歧义,以及优先匹配 initializer_list 重载可能带来的意外重载决议问题。

使用案例
使用案例一
  • std::initializer_list 的多种遍历方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <algorithm>
#include <initializer_list>
#include <iostream>
#include <string>
#include <vector>

// ============================================================
// 遍历方式一:基于范围的 for(最常用、最简洁)
// ============================================================
void traverse_range_for(std::initializer_list<int> nums) {
// 按值遍历,元素本身是 const int
for (int n : nums) {
std::cout << n << ' ';
}
std::cout << std::endl;
}

// ============================================================
// 遍历方式二:迭代器(begin/end),可用 const 引用避免拷贝
// ============================================================
void traverse_iterator(std::initializer_list<std::string> words) {
for (auto it = words.begin(); it != words.end(); ++it) {
// 迭代器指向 const 元素
std::cout << *it << ' ';
}
std::cout << std::endl;
}

// ============================================================
// 遍历方式三:下标式访问(借助 begin() 指针 + size())
// initializer_list 没有 operator[],需用 *(begin() + i)
// ============================================================
void traverse_index(std::initializer_list<int> nums) {
for (std::size_t i = 0; i < nums.size(); ++i) {
std::cout << *(nums.begin() + i) << ' ';
}
std::cout << std::endl;
}

// ============================================================
// 遍历方式四:配合 STL 算法(只读算法均可直接使用)
// ============================================================
void traverse_algorithm(std::initializer_list<int> nums) {
// 统计符合条件的元素个数
std::cout << "count = " << std::count_if(nums.begin(), nums.end(), [](int v) { return v > 0; }) << std::endl;
// 求和(只读遍历)
int total = 0;
std::for_each(nums.begin(), nums.end(), [&total](int v) { total += v; });
std::cout << "total = " << total << std::endl;
}

// ============================================================
// 遍历方式五:直接拷贝到标准容器后再操作(需要修改时)
// ============================================================
std::vector<int> to_vector(std::initializer_list<int> nums) {
// 元素只读,想修改必须先拷贝到具体的标准容器中
std::vector<int> v(nums); // 用迭代器区间构造
for (int& x : v) {
x *= 2; // 现在可以修改副本
}
return v;
}

int main() {
traverse_range_for({1, 2, 3, 4, 5});
traverse_iterator({"hello", "world", "cpp"});
traverse_index({10, 20, 30});
traverse_algorithm({-1, 2, -3, 4, 5});

auto doubled = to_vector({1, 2, 3});
for (int v : doubled) std::cout << v << ' ';
std::cout << std::endl;

return 0;
}

程序运行的结果如下:

1
2
3
4
5
6
1 2 3 4 5 
hello world cpp
10 20 30
count = 3
total = 7
2 4 6
使用案例二
  • std::initializer_list 的各种使用场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <algorithm>
#include <initializer_list>
#include <iostream>
#include <string>
#include <vector>

// ============================================================
// 使用场景一:普通函数接受任意数量同类型实参
// ============================================================

// 计算一组整数的总和;调用时可直接写 sum({1, 2, 3, 4, 5})
int sum(std::initializer_list<int> nums) {
int total = 0;
// initializer_list 支持基于范围的 for,元素为 const
for (int n : nums) {
total += n;
}
return total;
}

// 求最大值,演示空列表处理
int max_of(std::initializer_list<int> nums) {
if (nums.size() == 0) { // 用 size() 判断是否为空
return 0; // 约定空列表返回 0
}
return *std::max_element(nums.begin(), nums.end());
}

// ============================================================
// 使用场景二:构造函数重载 —— 初始化容器式对象
// ============================================================

class IntArray {
public:
// 用 initializer_list 构造:IntArray a{1, 2, 3};
IntArray(std::initializer_list<int> init) : data_(init) {
}

void print() const {
for (int v : data_) std::cout << v << ' ';
std::cout << '\n';
}

private:
std::vector<int> data_; // 内部用 vector 实际持有数据
};

// ============================================================
// 使用场景三:与变参模板配合,作为“同类型批量参数”的统一入口
// ============================================================

// 变参模板负责任意类型、任意个数
template <typename T>
T sum_recursive(T v) {
return v;
}

template <typename T, typename... Args>
T sum_recursive(T head, Args... tail) {
return head + sum_recursive(tail...);
}

// 提供一个 initializer_list 重载,便于对“同类型批量值”统一调用
int sum_unified(std::initializer_list<int> nums) {
int total = 0;
for (int n : nums) total += n;
return total;
}

template <typename... Args>
auto sum_unified(Args... args) -> decltype(sum_recursive(args...)) {
return sum_recursive(args...); // 不同个数/类型走变参模板
}

int main() {
// 使用场景一:花括号列表直接作为实参
std::cout << sum({1, 2, 3, 4, 5}) << '\n';
std::cout << sum({}) << '\n';
std::cout << max_of({3, 9, 2, 7}) << '\n';
std::cout << "---------------------" << std::endl;

// 使用场景二:构造函数使用 initializer_list
IntArray a{1, 2, 3, 4}; // 调用 initializer_list 构造
a.print();
std::cout << "---------------------" << std::endl;

// 使用场景三:统一入口
std::cout << sum_unified({1, 2, 3}) << '\n'; // 走 initializer_list 重载
std::cout << sum_unified(1, 2.5, 3) << '\n'; // 走变参模板
std::cout << "---------------------" << std::endl;

// 补充:直接用 initializer_list 初始化标准容器(即直接初始化列表)
std::vector<std::string> words{"hello", "world", "cpp"};
for (const auto& w : words) {
std::cout << w << ' ';
}
std::cout << '\n';

return 0;
}

程序运行的结果如下:

1
2
3
4
5
6
7
8
9
10
15
0
9
---------------------
1 2 3 4
---------------------
6
6
---------------------
hello world cpp

C++ 萃取机制用法浅析

类型萃取

核心概念

C++ 类型萃取(Type Traits)是指通过模板机制在编译期获取、判断或转换类型相关信息的一种技术,主要用于根据类型特征实现不同的编译期逻辑。从 C++ 11 开始,标准库在 <type_traits> 头文件中正式提供了丰富的类型萃取工具,例如判断类型是否为整型、是否为指针、是否具有虚析构函数,以及移除类型的 const、引用等修饰。类型萃取广泛应用于泛型编程、模板元编程和类型约束等场景,可以增强模板代码的通用性与类型安全性。值得一提的是,泛型编程、模板编程、模板元编程不是同一个概念,具体说明如下:

  • 泛型编程(Generic Programming):是一种编程思想,强调编写与具体数据类型无关的通用代码,使算法和数据结构能够适用于不同类型的数据。在 C++ 中,泛型编程不仅可以通过模板编程实现,还可以借助函数重载、概念(Concepts)等机制实现相关的通用设计。
  • 模板编程(Template Programming):指利用 C++ 的函数模板、类模板、变量模板等语言机制编写通用代码,实现类型参数化和代码复用。模板编程既可以用于实现泛型算法和容器,也可以用于编译期计算、类型操作等模板元编程场景。
  • 模板元编程(Template Metaprogramming,TMP):指利用模板机制在编译期进行计算、类型判断、类型转换或逻辑分支等操作,将部分计算和决策从运行期提前到编译期。例如,使用模板递归计算阶乘、使用类型萃取判断类型属性,以及通过 std::enable_if 实现条件编译。
使用案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <list>
#include <type_traits>
#include <typeinfo>

using namespace std;

template <typename T>
void printTraitsInfo(const T& t) {
cout << "------------begin------------" << endl;
cout << "type name: " << typeid(T).name() << endl;
cout << "is_void = " << is_void<T>::value << endl; // 类型是否是 void
cout << "is_class = " << is_class<T>::value << endl; // 类型是否是一个 class
cout << "is_object = " << is_object<T>::value << endl; // 类型是否是一个对象类型
cout << "is_pod = " << is_pod<T>::value << endl; // 是否是 POD 类型(只拥有成员变量,不拥有成员函数)
cout << "is_default_constructible = " << is_default_constructible<T>::value << endl; // 是否有默认构造函数
cout << "is_copy_constructible = " << is_copy_constructible<T>::value << endl; // 是否有拷贝构造函数
cout << "is_move_constructible = " << is_move_constructible<T>::value << endl; // 是否有移动构造函数
cout << "is_destructible = " << is_destructible<T>::value << endl; // 是否有析构函数
cout << "is_polymorphic = " << is_polymorphic<T>::value << endl; // 是否含有虚函数
cout << "has_trivial_default_constructor = " << has_trivial_default_constructor<T>::value << endl; // 是否有平凡默认构造
cout << "has_virtual_destructor = " << has_virtual_destructor<T>::value << endl; // 是否有虚析构函数
cout << "------------end------------" << endl;
}

class A {
public:
A() = default; // 默认构造函数
A(A&& ta) = delete; // 移动构造:如果不写delete,系统一般就会认为有这个成员函数
A(const A& ta) = delete; // 拷贝构造
virtual ~A() { // 虚析构
}
};

class B {
public:
int m_i;
int m_j;
};

class C {
public:
C(int val) { // 有参构造函数,编译器不会再提供默认构造函数
}
};

int main() {
printTraitsInfo(int());
printTraitsInfo(string());
printTraitsInfo(A());
printTraitsInfo(B());
printTraitsInfo(C(10));
printTraitsInfo(list<int>());
}

程序运行的结果如下(Visual Studio 运行环境):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
------------begin------------
type name: int
is_void = 0
is_class = 0
is_object = 1
is_pod = 1
is_default_constructible = 1
is_copy_constructible = 1
is_move_constructible = 1
is_destructible = 1
is_polymorphic = 0
has_trivial_default_constructor = 1
has_virtual_destructor = 0
------------end------------
------------begin------------
type name: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
is_void = 0
is_class = 1
is_object = 1
is_pod = 0
is_default_constructible = 1
is_copy_constructible = 1
is_move_constructible = 1
is_destructible = 1
is_polymorphic = 0
has_trivial_default_constructor = 0
has_virtual_destructor = 0
------------end------------
------------begin------------
type name: class A
is_void = 0
is_class = 1
is_object = 1
is_pod = 0
is_default_constructible = 1
is_copy_constructible = 0
is_move_constructible = 0
is_destructible = 1
is_polymorphic = 1
has_trivial_default_constructor = 0
has_virtual_destructor = 1
------------end------------
------------begin------------
type name: class B
is_void = 0
is_class = 1
is_object = 1
is_pod = 1
is_default_constructible = 1
is_copy_constructible = 1
is_move_constructible = 1
is_destructible = 1
is_polymorphic = 0
has_trivial_default_constructor = 1
has_virtual_destructor = 0
------------end------------
------------begin------------
type name: class C
is_void = 0
is_class = 1
is_object = 1
is_pod = 0
is_default_constructible = 0
is_copy_constructible = 1
is_move_constructible = 1
is_destructible = 1
is_polymorphic = 0
has_trivial_default_constructor = 0
has_virtual_destructor = 0
------------end------------
------------begin------------
type name: class std::list<int,class std::allocator<int> >
is_void = 0
is_class = 1
is_object = 1
is_pod = 0
is_default_constructible = 1
is_copy_constructible = 1
is_move_constructible = 1
is_destructible = 1
is_polymorphic = 0
has_trivial_default_constructor = 0
has_virtual_destructor = 0
------------end------------

迭代器萃取

核心概念

C++ 迭代器萃取(Iterator Traits)是指通过 std::iterator_traits 在编译期获取迭代器的相关类型信息,为泛型算法提供统一的类型访问接口。它主要提取迭代器的五种关联类型:迭代器类别(iterator_category)、元素类型(value_type)、差值类型(difference_type)、指针类型(pointer)和引用类型(reference)。迭代器萃取广泛应用于 STL 容器和泛型算法中,使算法能够根据迭代器的类型特征选择合适的实现方式,例如针对随机访问迭代器进行高效的距离计算。标准库从 C++ 98 起就提供了 std::iterator_traits,是 STL 泛型编程的重要基础。

使用案例
使用案例一
  • 获取迭代器的类型信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <iterator>
#include <list>
#include <typeinfo>
#include <vector>

using namespace std;

template <typename Iter>
void printIteratorInfo(Iter iter) {
using Category = typename iterator_traits<Iter>::iterator_category; // 获取迭代器的类别,如随机访问迭代器、双向迭代器
using ValueType = typename iterator_traits<Iter>::value_type; // 获取迭代器指向的元素类型
using DifferenceType = typename iterator_traits<Iter>::difference_type; // 获取两个迭代器之间距离的类型
using Pointer = typename iterator_traits<Iter>::pointer; // 获取迭代器对应的指针类型
using Reference = typename iterator_traits<Iter>::reference; // 获取迭代器对应的引用类型

cout << "Iterator Category: " << typeid(Category).name() << endl;
cout << "Value Type: " << typeid(ValueType).name() << endl;
cout << "Difference Type: " << typeid(DifferenceType).name() << endl;
cout << "Pointer Type: " << typeid(Pointer).name() << endl;
cout << "Reference Type: " << typeid(Reference).name() << endl;
}

int main() {
vector<int> vec = {1, 2, 3};
list<int> lst = {4, 5, 6};

cout << "Vector Iterator Info:" << endl;
printIteratorInfo(vec.begin());

cout << "\nList Iterator Info:" << endl;
printIteratorInfo(lst.begin());

return 0;
}

程序运行的结果如下(Visual Studio 运行环境):

1
2
3
4
5
6
7
8
9
10
11
12
13
Vector Iterator Info:
Iterator Category: struct std::random_access_iterator_tag
Value Type: int
Difference Type: __int64
Pointer Type: int * __ptr64
Reference Type: int

List Iterator Info:
Iterator Category: struct std::bidirectional_iterator_tag
Value Type: int
Difference Type: __int64
Pointer Type: int * __ptr64
Reference Type: int
使用案例二
  • 根据迭代器类型选择不同的算法实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* 迭代器萃取可以配合函数重载,根据迭代器类型选择不同的算法实现。
* 例如,随机访问迭代器可以通过相减直接计算距离,而其他类型的迭代器(如输入迭代器)通常需要逐步遍历。
*/

#include <iostream>
#include <iterator>
#include <list>
#include <vector>

using namespace std;

// 随机访问迭代器:通过相减直接计算距离
template <typename Iter>
typename iterator_traits<Iter>::difference_type myDistance(Iter first, Iter last, random_access_iterator_tag) {
cout << "Using random access iterator algorithm" << endl;
return last - first;
}

// 其他迭代器:通过逐步遍历计算距离
template <typename Iter, typename Category>
typename iterator_traits<Iter>::difference_type myDistance(Iter first, Iter last, Category) {
cout << "Using step-by-step traversal algorithm" << endl;
typename iterator_traits<Iter>::difference_type n = 0;
while (first != last) {
++first;
++n;
}
return n;
}

// 利用迭代器萃取获取迭代器类别
template <typename Iter>
typename iterator_traits<Iter>::difference_type myDistance(Iter first, Iter last) {
using Category = typename iterator_traits<Iter>::iterator_category;
return myDistance(first, last, Category{});
}

int main() {
vector<int> vec = {1, 2, 3, 4, 5};
list<int> lst = {1, 2, 3, 4, 5};

cout << "Vector distance: " << myDistance(vec.begin(), vec.end()) << endl;
cout << "List distance: " << myDistance(lst.begin(), lst.end()) << endl;

return 0;
}

程序运行的结果如下:

1
2
3
4
Vector distance: Using random access iterator algorithm
5
List distance: Using step-by-step traversal algorithm
5

代码分析

上述案例代码的核心原理: std::iterator_traits 负责提取迭代器的类型信息,iterator_category 则可以用于标签分发(Tag Dispatch),从而让泛型算法根据迭代器类别选择不同的实现方式,这也是 STL 泛型算法设计中的经典技术。值得注意的是,上述案例代码兼容 C++ 11。实际使用时,C++ 标准库已经提供了 std::distance,一般不需要自行实现 myDistance()。