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

时间:2014-11-30 16:58:48

标签: c++ vector iterator containers set-intersection

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

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

这应该将c设置为交叉点(ab),假设ab已排序。

但是,如果我只使用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()

无论如何,c.begin()的代码在clang下编译。

根据标准保证会发生什么?如果这个编译,那么可能会发生什么 - 也就是说,当begin()返回的迭代器最终递增超过向量的末尾,并且尝试访问指向的元素时,必须/可能发生什么?在这种情况下,符合标准的实现是否可以无提示地扩展向量,因此OutputIterator实际上是back_inserter的追加{{1}}?

我这主要是为了理解这个标准如何与迭代器一起工作:真正发生了什么,所以我可以使用STL超越复制粘贴。

3 个答案:

答案 0 :(得分:10)

back_inserter通过调用push_back来插入范围内的元素(这就是为什么你不能使用back_inserter的范围而不提供的范围push_back操作)。

因此,您不会关心超出范围的末尾,因为push_back会自动扩展容器。但是,使用begin()插入的情况并非如此。

如果您使用的是begin(),那么您必须确保目标范围足够大来容纳所有元素。如果不这样做会立即将您的代码传输到未定义行为的领域。

答案 1 :(得分:6)

它编译得很好,因为你从begin函数得到一个有效的迭代器,但是如果向量是空的,那么你将返回end迭代器,然后从那里继续。

如果目标向量已包含至少与您尝试添加的元素一样多,那么它将仅 ,然后它实际上会覆盖这些元素而不会添加新元素

添加元素正是back_inserter迭代器的作用,它返回的迭代器基本上对向量执行push_back

答案 2 :(得分:5)

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

传递c.begin()会导致值被覆盖,只有当容器c拥有足够的要素才能覆盖时才会有效。想象一下,c.begin()返回指向大小为0的数组的指针 - 然后在编写*out++ = 7;时会看到问题。

back_inserter 将每个指定的值添加<{strong> vector(通过push_back),并提供简明的方法使STL算法扩展范围 - 它适当地重载用于迭代器的运算符。

因此

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

set_intersection向其输出迭代器写入内容时调用未定义的行为,也就是说,当ab的集合交集不为空时。

  

在这种情况下,符合要求的实现是否可以无提示地扩展向量,因此begin()实际上是OutputIterator的追加back_inserter

当然。这是未定义的行为。 (这是一种幽默的方式告诉你,你不应该考虑使用它,不管对任何实现的影响。)