以前我们学迭代器。
学得很像尺子。
begin 是一头。
end 是另一头。
两头都是同一种东西。
都是迭代器。
所以你很自然会觉得。
“end 就是指向尾后的位置”。
C++20 说。
不一定。
当年:我们硬把“结束条件”塞进迭代器
最典型的例子。
是读输入流。
你不知道有多少个数字。
你只能读。
读到读不出来为止。
以前 STL 的方式。
是用一个“特殊的迭代器值”当 end。
你写代码的时候。
不容易看出它其实是哨兵。
线上啪一下:我在读文件,结果你让我先构造一个 end 迭代器
你写了个小项目。
从文件里读一串 int。
遇到 EOF 就停。
你不想写 while。
你想用 range-for。
C++20 给了你一个很干净的东西。
#include <ranges>
#include <sstream>
std::istringstream in("1 2 3");
for (int x : std::ranges::istream_view<int>(in)) {
// ...
}
这里的关键点是。
istream_view 的 end。
不是一个“同类型的迭代器”。
它是一个 sentinel。
更像一个标记。
意思是。
“到这里就停”。
sentinel 是什么
sentinel 你可以先把它理解成。
“终点标志”。
它不需要能 ++。
也不需要能解引用。
它只需要能和迭代器比较。
比如。
“你读到 EOF 了吗”。
比如。
“你读到 \0 了吗”。
它更贴近现实。
因为现实里的结束条件。
很多不是地址。
而是状态。
为什么这件事很值钱
因为它让 ranges 可以表达更多东西。
它不再要求。
必须有一个“尾后迭代器”。
有些范围就没有这个概念。
比如输入流。
比如某些生成器。
比如某些按条件停止的遍历。
关键结论
C++20 ranges 把“结束”从迭代器里拆出来。
用 sentinel 单独表达。
begin/end 不同类型。
不再是异常。
而是常态。
小结
你可以继续把 range-for 当成老朋友。
但你要记住。
在 ranges 的世界里。
end() 可能只是一个“停下来的信号”。