我一直在使用高度简洁直观的C ++语法来查找两个已排序vector
的交集,并将结果放在第三个vector
中:
vector<bar> a,b,c;
//...
std::set_intersection(a.begin(),a.end(),b.begin(),b.end(),
std::back_inserter(c));
这应该将c
设置为交叉点(a
,b
),假设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()
。
无论如何,c.begin()
的代码在clang下编译。
根据标准保证会发生什么?如果这个编译,那么可能会发生什么 - 也就是说,当begin()
返回的迭代器最终递增超过向量的末尾,并且尝试访问指向的元素时,必须/可能发生什么?在这种情况下,符合标准的实现是否可以无提示地扩展向量,因此OutputIterator
实际上是back_inserter
的追加{{1}}?
我这主要是为了理解这个标准如何与迭代器一起工作:真正发生了什么,所以我可以使用STL超越复制粘贴。
答案 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
向其输出迭代器写入内容时调用未定义的行为,也就是说,当a
和b
的集合交集不为空时。
在这种情况下,符合要求的实现是否可以无提示地扩展向量,因此begin()实际上是
OutputIterator
的追加back_inserter
?
当然。这是未定义的行为。 (这是一种幽默的方式告诉你,你不应该考虑使用它,不管对任何实现的影响。)