
知识地图:"精通"c++:模版元编程
本文专注大厂面试拆解,希望对你有帮助,
如果在面试中遇到任何问题,欢迎留言,免费答疑
“迭代器的本质,是抽象。抽象的目的是屏蔽细节,统一接口,从而实现复用。” —— 一位架构师的 STL 思维笔记
vector 上跑?你是不是曾经写过类似这样的排序逻辑?
std::sort(vec.begin(), vec.end());
非常顺畅。但当你想把 std::list<int> 排序时,却发现无法复用原来的 sort:
std::list<int> lst = {, , };
std::sort(lst.begin(), lst.end()); // ❌ 编译报错
这是为什么?
难道 STL 的算法只能服务于 vector?那和“泛型编程”的初衷岂不是背道而驰?
其实,答案的关键就在于 —— 迭代器(iterator)是怎么设计的?
小提示:
在讲迭代器之前,我们先快速回顾一下 STL 的三大支柱:
vector、list、map。sort()、find()、for_each()。而 算法要做到与“容器类型”无关,靠的正是迭代器的抽象设计。
你可以这样理解:
泛型算法之所以能在
vector、list、deque等不同容器上复用,并不是直接操作容器对象本身,而是通过一对iterator来间接操作容器中的元素范围。
比如:
template<typename Iterator>
void my_print(Iterator first, Iterator last) {
while (first != last) {
std::cout << *first << std::endl;
++first;
}
}
这段代码,不关心你传进来的是 vector<int>::iterator 还是 list<string>::iterator。只要满足最基本的迭代器语义:解引用 *it,自增 ++it,就可以“统一操作”。
这就像是在不同品牌的硬盘之间插上一层兼容驱动:数据源可以多样,操作方式可以统一。
我们再看开头提到的问题:为什么 std::sort() 无法用于 list?
这其实不是 sort 排斥 list,而是它依赖的迭代器类型需要“随机访问”,而 list 提供的是“双向访问”:
容器 | 迭代器类型 | 是否支持 sort |
|---|---|---|
vector | 随机访问(Random Access) | ✅ 支持 |
deque | 随机访问 | ✅ 支持 |
list | 双向(Bidirectional) | ❌ 不支持 |
sort 是算法库的一部分,不会因容器的实现方式而改变逻辑。所以它通过 iterator_traits 来静态判断迭代器类型。如果达不到要求,直接拒绝编译,防止错误行为。
这也进一步说明了:STL 的算法和容器是“解耦”的,连接它们的核心正是 iterator 的类型与行为。
C++ 标准库将迭代器划分为五种“访问能力”,形成了一种类型层级结构:
istream_iterator)ostream_iterator)list)vector)算法库通过 iterator_traits<Iter>::iterator_category 检测迭代器能力,从而决定可用策略:
template<typename Iter>
void do_sort(Iter first, Iter last) {
static_assert(std::is_base_of_v<
std::random_access_iterator_tag,
typename std::iterator_traits<Iter>::iterator_category
>, "sort 需要随机访问迭代器");
}
template<class Iterator>
void advance(Iterator& i) {
if (is_random_access_iterator(i))
advanceRAI(i,n);
if (is_bidirectional_iterator(i))
advanceBI(i,n);
else
advanceII(i,n);
}
这样在执行时期才决定使用哪一个版本, 会影响程序效率。
最好能够在编译期就选择正确的版本。
而重载这个函数机制可以达成这个目标。
设计如下:
如果traits有能力萃取出迭代器的种类,我们便可利用这个"迭代器类型"相应型别作为advancexx的第三个参数,
而这个相应型别必须是一个class type,不能只是数值号码类的东西,因为编译器需依赖它来进行重载决议。
先测试,后开发
int main() {
iterator<input_iterator_tag> input;
iterator<output_iterator_tag> output;
iterator<forward_iterator_tag> forward;
iterator<bidiectional_iterator_tag> bidect;
iterator<random_access_iterator_tag> random;
advance(input, 10);
advance(output, 10);
advance(forward, 10);
advance(bidect, 10);
advance(random, 10);
int *p=NULL;
advance(p,10);
return 0;
}
why
对外暴露接口:
// 对外接口
template<class InputIterator, class Distance>
inline void advance(InputIterator &i, Distance n) {
// 通过Ierator_traits询问它的iterator_category是谁
typedef typename Iterator_traits<InputIterator>::iterator_category category;
__advance(i, n, category()); // 各型别的重载
}
Iterator_traits萃取机制:
// traits 型别
template<class I>
struct Iterator_traits {
typedef typename I::iterator_category iterator_category;
};
// 针对原生指针设计的"偏特化版"
template<class I>
struct Iterator_traits<I *> {
typedef random_access_iterator_tag iterator_category;
};
template<class I>
struct Iterator_traits<const I *> {
typedef random_access_iterator_tag iterator_category;
};
https://github.com/Light-City/CPlusPlusThings/blob/master/src_analysis/stl/iterator.md
函数重载
// forward iterator
template<class ForwardIterator, class Distance>
inline typename Iterator_traits<ForwardIterator>::iterator_category
__advance(ForwardIterator &i, Distance n,
forward_iterator_tag) {
std::cout << "forward tag" << std::endl;
return forward_iterator_tag();
}
// bidrectional iterator
template<class BidiectionalIterator, class Distance>
inline typename Iterator_traits<BidiectionalIterator>::iterator_category
__advance(BidiectionalIterator &i, Distance n,
bidiectional_iterator_tag) {
std::cout << "bidrectional tag" << std::endl;
return bidiectional_iterator_tag();
}
为什么 iterator 能成为 STL 的“胶水”?
我们用一句话概括它的三个核心作用:
功能 | 作用 |
|---|---|
抽象指针 | 将容器的实现细节屏蔽掉 |
解耦算法 | 让算法不依赖具体容器类型 |
静态策略 | 通过类型 traits 优化编译时行为 |
从架构设计角度来看,它提供了非常典型的策略模式 + traits 元编程 + 分层抽象,这也是现代泛型库设计的重要思想基础。
欢迎在评论区留言讨论。
#C++ #STL源码解析 #泛型编程 #架构设计 #iterator机制 #中高级C++ #软件工程思维
如果你觉得这篇文章有帮助,欢迎点赞、分享,让更多人理解 STL 背后的设计哲学。