pop_back()是否真的会使std :: vector上的所有迭代器无效?
std::vector<int> ints; // ... fill ints with random values for(std::vector<int>::iterator it = ints.begin(); it != ints.end(); ) { if(*it < 10) { *it = ints.back(); ints.pop_back(); continue; } it++; }
该代码不起作用,因为调用pop_back()
时,it
无效。但是我没有在std :: vector :: pop_back()
中找到任何有关迭代器无效的文档。
我们是否有与此相关的链接?
解决方案
回答
迭代器仅在重新分配存储空间时才无效。 Google是朋友:请参见脚注5.
代码因其他原因无法正常工作。
回答
在此处查看信息(cplusplus.com):
Delete last element Removes the last element in the vector, effectively reducing the vector size by one and invalidating all iterators and references to it.
回答
错误是,当"它"指向向量的最后一个元素时,如果该元素小于10,则将删除最后一个元素。现在," it"指向ints.end(),下一个" it ++"将指针移至ints.end()+ 1,因此现在" it"从ints.end()移开,我们将进行无限循环扫描所有记忆 :)。
回答
调用pop_back()会删除向量中的最后一个元素,因此对该元素的迭代器无效。调用pop_back()不会使最后一个元素之前的项目的迭代器无效,只有重新分配才能做到这一点。摘自Josuttis的" C ++标准库参考":
Inserting or removing elements invalidates references, pointers, and iterators that refer to the following element. If an insertion causes reallocation, it invalidates all references, iterators, and pointers.
回答
以下是SGI STL文档(http://www.sgi.com/tech/stl/Vector.html)的引文:
[5]重新分配向量的迭代器后,其迭代器将无效。此外,在向量中间插入或者删除元素会使指向插入或者删除点之后的元素的所有迭代器无效。因此,如果使用reserve()预分配与向量将使用的内存一样多的内存,并且所有插入和删除都在向量的末尾,则可以防止向量的迭代器无效。
我认为,pop_back仅会使指向最后一个元素和end()迭代器的迭代器无效。我们确实需要查看代码失败的数据,以及代码无法确定正在发生什么的方式。据我所知,代码应该可以解决此类代码中的常见问题,即@mikhaild指出,迭代器中元素和++的删除是在同一迭代中进行的。但是,在此代码中并非如此:调用pop_back时不会发生++。
当它指向最后一个元素,并且最后一个元素小于10时,可能仍然会发生某些不良情况。它可能仍然有效,但是无法保证。
回答
"官方规范"是C ++标准。如果我们无权获取C ++ 03的副本,则可以从委员会的网站获取最新的C ++ 0x草案:http://www.open-std.org/jtc1/sc22/wg21/ docs / papers / 2008 / n2723.pdf
容器要求的"操作语义"部分指定pop_back()等效于{迭代器i = end(); - 一世;擦除(i); }。擦除的[vector.modifiers]部分显示"效果:在擦除点或者之后使迭代器和引用无效。"
如果要使用直觉参数,则pop_back是不成功的(因为不允许破坏标准容器中的value_types引发异常),因此它无法进行任何复制或者分配(因为它们可以引发),这意味着我们可以猜测删除元素的迭代器和结束迭代器均无效,但其余的则无效。
回答
这是回答,直接来自圣典:
23.2.4.2 A vector satisfies all of the requirements of a container and of a reversible container (given in two tables in 23.1) and of a sequence, including most of the optional sequence requirements (23.1.1).
23.1.1.12 Table 68 expressiona.pop_back() return typevoid operational semanticsa.erase(--a.end()) containervector, list, deque
请注意,a.pop_back等效于a.erase(-a.end())。查看向量在擦除方面的详细信息:
23.2.4.3.3 - iterator erase(iterator position) - effects - Invalidates all the iterators and references after the point of the erase
因此,一旦调用pop_back,到先前最后一个元素(现在不再存在)的所有迭代器都将失效。
查看代码,问题在于,当我们删除最后一个元素并且列表变为空时,我们仍将其递增并走出列表的末尾。
回答
(我使用的是C ++ 0x工作草案中使用的编号方案,可在此处获得
第732页的表94表示pop_back(如果它存在于序列容器中)具有以下作用:
{ iterator tmp = a.end(); --tmp; a.erase(tmp); }
23.1.1第12点指出:
Unless otherwise speci?ed (either explicitly or by de?ning a function in terms of other functions), invoking a container member function or passing a container as an argument to a library function shall not invalidate iterators to, or change the values of, objects within that container.
都使用end来访问end()都没有这种效果,但是,delete():
23.2.6.4(关于vector.erase()点4):
Effects: Invalidates iterators and references at or after the point of the erase.
因此,总而言之:按照标准,pop_back()只会使对最后一个元素的迭代器无效。
回答
如果pop_back()指向向量中的最后一项,则只会使它无效。因此,只要向量中的最后一个int小于10,代码就会失败,如下所示:
它= ints.back(); //将it设置为它已经具有的值
ints.pop_back(); //使迭代器无效
继续; //循环并访问无效的迭代器
回答
" pop_back()"仅使指向最后一个元素的迭代器无效。从C ++标准库参考中:
Inserting or removing elements invalidates references, pointers, and iterators that refer to the following element. If an insertion causes reallocation, it invalidates all references, iterators, and pointers.
因此,要回答问题,不,它不会使所有迭代器无效。
但是,在代码示例中,当指向最后一个元素且该值小于10时,它可以使它无效。在这种情况下,Visual Studio debug STL将迭代器标记为无效,并进一步检查其是否等于end()将显示一个断言。
如果迭代器被实现为纯指针(就像在所有非调试STL矢量情况下那样),则代码应该可以正常工作。如果迭代器不仅仅是指针,那么代码将无法正确处理删除最后一个元素的情况。
回答
我们可能要考虑使用擦除的返回值,而不是将back元素交换到已删除位置,然后弹出。对于序列擦除,返回一个迭代器,该迭代器将元素指向一个要删除的元素之外的元素。请注意,与原始算法相比,此方法可能导致更多的复制。
for(std::vector<int>::iterator it = ints.begin(); it != ints.end(); ) { if(*it < 10) it = ints.erase( it ); else ++it; }
std :: remove_if
也可以作为替代解决方案。
struct LessThanTen { bool operator()( int n ) { return n < 10; } }; ints.erase( std::remove_if( ints.begin(), ints.end(), LessThanTen() ), ints.end() );
" std :: remove_if"是稳定的(就像我的第一个算法一样),因此它可能不是最有效的方法,但是它很简洁。