我一直在使用高度简洁直观的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()
的代码是在 clang 下编译的。
在标准下保证会发生什么?如果这样编译,可能会发生什么 - 也就是说,当 c.begin()
返回的迭代器最终递增到向量末尾,并尝试访问指向的元素时,必须/可能会发生什么?在这种情况下,符合标准的实现是否可以静默地扩展向量,以便begin()
实际上是像back_inserter
一样的追加OutputIterator
?
我问这个问题主要是为了了解标准如何与迭代器一起工作:到底发生了什么,所以我可以使用 STL 超越复制和粘贴。
back_inserter
通过调用push_back
将元素插入到范围内(这就是为什么你不能将back_inserter
与不提供push_back
操作的范围一起使用的原因(。
因此,您不会关心超过范围的末尾,因为push_back
会自动扩展容器。但是,使用 begin()
插入时并非如此。
如果您使用的是 begin()
,则必须确保目标范围足够大以容纳所有元素。如果不这样做,则会立即将您的代码传输到未定义行为的领域。
它编译得很好,因为你从begin
函数中得到一个有效的迭代器,但如果向量为空,那么你将得到end
迭代器,然后从那里继续。
仅当目标向量已经包含至少与您尝试添加的元素一样多的元素时,它才会起作用,然后它实际上会覆盖这些元素而不是添加新元素。
添加元素正是back_inserter
迭代器所做的,它返回一个基本上在向量上push_back
的迭代器。
输出迭代器的重要要求是它对于输出)
的范围[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
将某些内容写入其输出迭代器,即当 a
和 b
的集合交集不为空时,调用未定义的行为。
在这种情况下,符合要求的实现是否可以静默地扩展向量,以便 begin(( 实际上是像
back_inserter
一样的附加OutputIterator
?
答案是肯定的。这是未定义的行为。(这是一种幽默的方法,告诉你,你甚至不应该考虑使用它,不管对任何实现的影响。