先问一个问题:你写 C++ 的时候,用过 std::distance 这个函数吧?它看起来像是专门用来算两个迭代器之间距离的,似乎挺方便。不过需要警惕的是,它远没有你想象的那么“万能”——用错了地方,性能损耗和逻辑陷阱会接踵而至。
先说几点核心判断:std::distance 只对随机访问迭代器才高效;用在 list 或 forward_list 上会退化成 O(n) 遍历,别当它是“万能距离函数”。

std::distance 在不同容器上的时间复杂度差异
它的底层行为完全取决于迭代器类别——这跟它是哪种“体质”密切相关。对于 std::random_access_iterator(比如 vector、string、array 的迭代器),它直接做减法,复杂度是 O(1);而对于 std::forward_iterator(比如 list、forward_list),只能靠 ++ 一步步数过去,复杂度直降到 O(n)。
- 对
vector:用std::distance(it1, it2)和it2 - it1效果一致,推荐后者,更直白,零开销。 - 对
list:必须用std::distance,因为不能写it2 - it1(编译不过)。但你要清楚,每次调用都是一次完整的顺序遍历。 - 对
unordered_map:它的迭代器是前向的,std::distance可用但慢,且结果在实际业务中没什么意义——哈希表本身就不保证内部顺序,数出来的数字能说明什么?
std::distance 的两个常见误用场景
现实中的坑往往不会写在文档里。第一个常见错误是把迭代器的顺序传反了。如果 first 实际上在 last 后面,返回值是未定义行为——它不会返回负数,而是直接让你进入未定义区域。第二个错误更隐蔽:跨容器使用。哪怕两个 vector 类型完全相同,写 std::distance(v1.begin(), v2.begin()) 也是非法的,这跟迭代器减法的要求一样:它们必须属于同一序列。
- 确保
first可达last,也就是first在last之前或相等。 - 两个迭代器必须来自同一容器,或至少是同一序列(比如同一个
std::string的两个子范围)。 - 不要对临时容器调用,例如
std::distance(vec.begin(), (vec | std::views::filter(...)).begin())—— 视图的 begin() 迭代器和原容器根本不兼容,这是前车之鉴。
替代方案:什么时候该避开 std::distance
如果你已经知道容器类型,并且需要频繁计算距离,优先用容器提供的自有接口。对于 std::vector,有 size()、capacity(),索引差就是距离;std::string 可以用 .substr() 的起始位置来推导。至于 std::list,如果你发现需要频繁查它的位置,那可能表示设计本身存在问题——换个思路,考虑改用 std::vector 或者加个缓存索引。
几个实践建议:
- 对随机访问容器:直接用指针或迭代器减法,语义清晰、零开销。
- 对关联容器(比如
map、set):它们不提供位置概念,std::distance虽然能编译通过,但结果依赖内部红黑树结构,在业务上没有实际意义。 - 如果想判断两个迭代器是否“相邻”,别写
std::distance(a, b) == 1,改用std::next(a) == b,更安全也更通用。
最后,还有一个容易被忽略的细节:std::distance 的返回类型是 std::iterator_traits,通常是 long long 或 ptrdiff_t,和 size_t 不兼容。强制转成 unsigned 可能截断负值(虽然正常用法本就不该出现负值),但也容易触发 signed/unsigned 比较的警告。