C++ 算法库 <algorithm>中的equal函数的参数疑惑
为什么第二个容器只传 begin()?
学习 <algorithm> 中的 std::equal 时,遇到了这样的调用:
1 | |
既然是在比较两个容器的元素,为什么 v1 要传 begin() 和 end(),而 v2 只传一个 begin() 就够了?如果没有 v2.end(),函数怎么知道什么时候结束?
关键在于,这个重载以第一个范围的长度为准。v1.begin() 和 v1.end() 确定要比较的范围,v2.begin() 确定第二个范围的起点;第二个范围需要参与比较的元素数量,由第一个范围决定。标准草案中的定义
两个迭代器同步前进
begin() 指向第一个元素,end() 指向最后一个元素之后的位置,不能解引用。因此 [v1.begin(), v1.end()) 是一个左闭右开的范围。
可以把三个迭代器的版本理解成下面的写法:
1 | |
这里是帮助理解参数含义的示意实现,不是某个标准库的源码,也不能据此认为所有标准库实现都会按这个循环逐项执行。
每比较一对元素,两个迭代器就一起向后移动。第一个迭代器到达 last1,比较就可以结束。若第一个范围有 N 个元素,第二个范围参与比较的就是从 first2 开始的 N 个元素。
所以,不传 v2.end() 并不意味着函数自动知道了 v2 的大小。它只拿到了一个起点,调用者需要保证从这个位置开始有足够的元素。
v2 更长或更短时会怎样?
先看第二个容器更长的情况:
1 | |
比较关系如下:
1 | |
前三个元素对应相等,函数就返回 true。因此,这个调用不能单独用来判断两个完整容器是否相等。
如果第二个容器更短,例如 v1 是 {1, 2, 3},而 v2 只有 {1, 2},那么调用就不满足这个重载的范围要求,会产生未定义行为。不能把它当成一次安全的比较,也不能指望它自动返回 false。即使某次运行没有崩溃,也不能说明这种用法正确。Microsoft 标准库文档对长度问题的说明
对于这里从 v2.begin() 开始的调用,必须保证:
1 | |
这个条件保证第二个范围足够长,但要判断两个完整容器相等,还需要它们长度相同。
也可以把两个 end() 都传进去
C++14 起,std::equal 提供了接收四个迭代器的重载:
1 | |
两个范围都给出了边界,函数就能同时考虑长度和对应元素。长度不同返回 false;长度相同,还要检查对应元素是否相等。四迭代器版本的说明
这也正是当初增加该重载的原因之一:用三个迭代器比较完整序列时,很容易漏掉长度检查,导致错误结果或者未定义行为。相关标准提案 N3671
下面是一个可以直接运行的 C++14 示例:
1 | |
输出为:
1 | |
如果只是在比较这里这样的两个同类型 vector,直接使用 v1 == v2 就很清楚;需要指定比较范围时,再使用 std::equal。
再回头看最初的三个参数,就能对应起来:前两个参数给出第一个完整范围,第三个参数给出第二个范围的起点。第二个范围的比较长度由第一个范围隐含确定,范围是否足够长则由调用者负责。


