如果我使用vector::begin()而不是std::back_inserter(vector)来输出set_intersection,会发生什么?

10

我一直在使用简洁而直观的C++语法来查找两个已排序的vector的交集,并将结果放入第三个vector中:

vector<bar> a,b,c;
//...
std::set_intersection(a.begin(),a.end(),b.begin(),b.end(),
                      std::back_inserter(c));

假设ab已经排序,这将设置c为交集(a,b)。

但是如果我只使用c.begin()会怎样呢?(我记得在某个地方看到过一个例子,所以我这样做了):

 std::set_intersection(a.begin(),a.end(),b.begin(),b.end(),
                       c.begin());

set_intersection函数期望该参数为OutputIterator类型。标准只要求c.begin()返回一个forward iterator,可能是OutputIterator,也可能不是。

即使带有c.begin()的代码可以在clang编译通过,但这个标准究竟保证了什么呢?如果代码能够成功编译,那么当迭代器c.begin()最终增加到超过向量长度后,尝试访问指针指向的元素时,必须/可能发生什么事情呢?符合规范的实现是否可以默默地扩展向量,使得begin()实际上是一种追加型OutputIterator,类似于back_inserter

我的问题主要是想了解标准与迭代器之间的关系,以便在使用STL时不再局限于简单的复制和粘贴。


1
“Vector”和“std::vector”是一样的吗? - Walter
@Walter 是的,已经修复了,谢谢。 - kdog
3个回答

13
back_inserter 插入元素时会调用 push_back (因此你不能在不提供push_back 操作的范围内使用back_inserter)。
因此,当使用 push_back 时,你不必担心超出范围,因为它会自动扩展容器。然而,使用begin()操作进行插入则不同。
如果你使用了begin()操作,则必须确保目标范围足够大以容纳所有元素。否则,代码将立即进入未定义行为的领域。

8

它可以正常编译,因为您从begin函数中获得了一个有效的迭代器,但如果向量为空,则会返回end迭代器,然后从那里继续。

它只有在目标向量已经包含至少与您尝试添加的元素一样多的元素时才能工作,并且它实际上将覆盖这些元素而不是添加新元素。

而添加元素就是back_inserter迭代器所做的事情,它返回一个基本上对向量进行push_back操作的迭代器。


5
一个输出迭代器的重要要求是它对于范围
[out, out+输出大小) 是有效且可写的。

传递 c.begin() 会导致值被覆盖,这仅在容器 c 包含足够的元素以进行覆盖时才起作用。想象一下,如果 c.begin() 返回一个大小为0的数组指针,则在编写 *out++ = 7; 时将会出现问题。

back_inserter 每个分配的值添加一个vector(通过push_back)并提供了一种简洁的方法来扩展STL算法的范围-它适当地重载了用于迭代器的运算符。

因此

 std::set_intersection(a.begin(),a.end(),b.begin(),b.end(),
                       c.begin());
set_intersection在将ab的交集写入输出迭代器时,一旦写入内容,就会产生未定义的行为。

在这种情况下,符合规范的实现是否可以默默地扩展向量,以使begin()实际上成为类似于back_inserter的追加OutputIterator呢?

当然可以。这是一种幽默的方式告诉您,您不应该考虑使用它,无论对任何实现产生的影响如何,因为这是未定义的行为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接