std::distance 返回两个迭代器间调用 operator++ 所需的最少步数,是带符号整数类型,对随机访问迭代器为 o(1),对前向/双向迭代器为 o(n),不检查有效性,不可用于不同容器或不可达迭代器。

std::distance 在 C++ 中到底返回什么
它返回两个迭代器之间的“步数”,不是字节差也不是地址差,而是调用 operator++ 从第一个迭代器走到第二个迭代器所需的最少次数。对随机访问迭代器(如 std::vector::iterator)是 O(1),对双向或前向迭代器(如 std::list::iterator)是 O(n) —— 这点不注意容易在循环里写出隐式性能陷阱。
常见错误现象:std::distance(it_end, it_begin) 返回负数?错——它永远返回无符号类型 std::iterator_traits<it>::difference_type</it>,实际是带符号整数(通常是 long 或 long long),但如果你传反了顺序,结果就是负值,不会报错也不会断言。
- 必须确保
it_begin可达it_end(对单向迭代器尤其关键;传入不可达的两个std::list迭代器会无限循环) - 不能用于不同容器的迭代器之间(行为未定义)
- 对空容器,
std::distance(c.begin(), c.end())安全且为 0
为什么 vector::end() - vector::begin() 有时比 std::distance 更快
因为 std::vector::iterator 是随机访问迭代器,支持算术运算,v.end() - v.begin() 直接做指针减法,汇编级一条指令。而 std::distance(v.begin(), v.end()) 是模板函数,编译器虽常能内联优化成同样结果,但前提是它能推导出迭代器类别;如果迭代器类型被擦除(比如藏在模板参数里又没约束),就可能退化为通用版本,走循环计数。
- 优先用
it2 - it1(仅限随机访问迭代器) - 通用代码中坚持用
std::distance,别自己写while (it != end) { ++count; ++it; } - Clang/GCC 在 -O2 下对
std::distance随机访问迭代器基本都优化掉,但别依赖——显式减法更直白、更少歧义
std::distance 编译失败的三个典型原因
错误信息常是 no matching function for call to 'distance' 或类似 SFINAE 拒绝,本质是迭代器类型不满足要求。
立即学习“C++免费学习笔记(深入)”;
- 传入裸指针但没加
std::iterator_traits特化支持(C++17 起已支持,但老标准或自定义指针包装类可能不行) - 迭代器是输入迭代器(如
std::istream_iterator),而std::distance要求至少是前向迭代器 - 用了 const 迭代器和非 const 迭代器混搭(如
std::distance(c.cbegin(), c.begin())),类型不匹配导致模板推导失败
示例修复:std::distance(c.cbegin(), c.cend()) 或统一用 auto begin = c.begin() 后再传入。
替代方案:size() 和 std::ranges::distance(C++20)
container.size() 比 std::distance(c.begin(), c.end()) 快得多,且语义明确——但它只适用于有 size() 成员函数的容器(std::vector、std::string 等),不适用于 std::list(C++11 后是 O(1),但早期是 O(n))、std::forward_list 或任意范围(如子数组视图)。
C++20 的 std::ranges::distance 更泛化:接受任何 range(包括 initializer_list、C 风格数组、生成器),且对 sized_range 自动用 size(),否则才 fallback 到迭代计数。
- 已有容器且确定支持
size(),直接用c.size() - 写泛型算法时,用
std::ranges::distance(r)(C++20)或继续用std::distance(r.begin(), r.end())(兼容旧标准) - 注意
std::ranges::distance对输入范围(如std::istream_view)仍是 O(n),且无法提前知道是否耗尽
最易被忽略的一点:std::distance 不检查迭代器有效性。传入悬垂迭代器或已失效的 end(),行为未定义——它不会抛异常,也不会 assert,只会静默出错或崩溃。











