如果我使用 vector::begin() 而不是 std::back_inserter(vector) 来输出set_



我一直在使用高度简洁直观的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()的代码是在 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将某些内容写入其输出迭代器,即当 ab 的集合交集不为空时,调用未定义的行为。

在这种情况下,符合要求的实现是否可以静默地扩展向量,以便 begin(( 实际上是像 back_inserter 一样的附加OutputIterator

答案是肯定的。这是未定义的行为。(这是一种幽默的方法,告诉你,你甚至不应该考虑使用它,不管对任何实现的影响。

最新更新