简体   繁体   English

如果向量有足够的空间(通过储备创建),std :: vector :: insert()是否会使迭代器无效?

[英]Does std::vector::insert() invalidate iterators if the vector has enough room (created through reserve)?

Answering How to self-copy a vector? 回答如何自我复制载体? has got me a bit confused about iterator invalidation. 使我对迭代器无效感到困惑。 Some literature says "if you use insert, push_back, etc. consider all iterators invalid". 一些文献说:“如果使用insert,push_back等,则认为所有迭代器均无效”。 Thats clear, it might cause the vector to grow which invalidates iterators. 显然,这可能会导致向量增长,从而使迭代器无效。 What about the special case where I know there is going to be enough room? 我知道会有足够的空间怎么办?

first try: 第一次尝试:

myvec.reserve(myvec.size()*3);  //does this protect me from iterator invalidation?
vector<string>::iterator it = myvec.end();    
myvec.insert(myvec.end(), myvec.begin(), it);
myvec.insert(myvec.end(), myvec.begin(), it);

After some excellent answers second try: 经过一些出色的回答后,请再次尝试:

auto size = myvec.size();
myvec.reserve(size*3);  //does this protect me from iterator invalidation?  
myvec.insert(myvec.end(), myvec.begin(), myvec.begin()+size);
myvec.insert(myvec.end(), myvec.begin(), myvec.begin()+size);

After more excellent answers third try: 经过更出色的回答后,第三次尝试:

auto size = myvec.size();
myvec.reserve(size*3);  //does this protect me from iterator invalidation?  
back_insert_iterator< vector<string> > back_it (myvec);
copy (myvec.begin(),myvec.begin()+size,back_it);
copy (myvec.begin(),myvec.begin()+size,back_it);

This quote from Josuttis' "C++ Standard Library Reference": 引用自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. 如果插入导致重新分配,它将使所有引用,迭代器和指针无效。

suggests that my code is safe and defined behavior. 建议我的代码安全且行为明确。 Is there a passage in the standard which guaranties this? 标准中是否有段落可以保证这一点?

The past-the-end iterator is always a bit special. 过去的迭代器总是有点特殊。 I'd be careful. 我会注意的 The standard says this (23.3.6.5): 该标准说(23.3.6.5):

If no reallocation happens, all the iterators and references before the insertion point remain valid. 如果没有发生重新分配,则插入点之前的所有迭代器和引用均保持有效。

The key here is "before the insertion point". 此处的关键是“插入点之前”。 Since your original it is not before the insertion point (since it is the insertion point), I wouldn't bank on it remaining valid. 因为你原来it不是将插入点之前(因为它插入点),我不会行其上剩余的有效。

Although it is true that insertions into a vector won't cause reallocation as long as the capacity is not exceeded, and won't invalidate iterators to elements before the insertion point (which is arguably the case of end() , as @KerrekSB pointed out), Table 100 of the C++11 Standard (Paragraph 23.2.3) specifies the following precondition for the a.insert(p,i,j) function for sequence containers: 尽管确实可以做到,只要不超过容量,插入向量不会引起重新分配,并且不会使插入点之前的元素的迭代器无效(这可以说是end()的情况,正如@KerrekSB指出的那样) out),C ++ 11标准的表100(第23.2.3段a.insert(p,i,j)为序列容器的a.insert(p,i,j)函数指定了以下前提条件

[...] pre: i and j are not iterators into a. [...] pre:i和j不是a的迭代器。 [...] [...]

In your case, they clearly are, which makes me think that program has Undefined Behavior. 在您的情况下,它们显然是,这使我认为程序具有未定义的行为。

Iterators should not be invalidated mid function. 迭代器不应在功能中间失效。 The idea that memory may be relocated doesn't hold up, because you cannot use realloc on objects with non-trivial constructors. 内存可能会被重新分配的想法不会成立,因为您不能在具有非平凡构造函数的对象上使用realloc Even if construction was a not an issue, it would still have to copy the initial sequence twice in the worst case, negating any benefits in the average case. 即使构造不是问题,在最坏的情况下,它仍然必须复制两次初始序列,而在一般情况下,则没有任何好处。

Point being, it doesn't make sense to implement it that way; 要点是,以这种方式实现它没有任何意义; an alloc , copy , free is almost certainly done, regardless of what the standard says. 不管标准怎么说,几乎都可以确定alloccopyfree

This is safe because v.begin() and v.end() are always current. 这是安全的,因为v.begin()v.end()始终是最新的。

v.insert(v.end(), v.begin(), v.end());
v.insert(v.end(), v.begin(), v.end());

This is not. 这不是。

vector<foo>::iterator i = v.begin();
vector<foo>::iterator j = v.end();
v.insert(v.end(), i, j);
v.insert(v.end(), i, j);

However, self insertion can be wonky. 但是,自我插入可能会很不稳定。 Try the following under GCC. 在GCC下尝试以下操作。 The self insertion gives an incorrect result only if enough memory is available (not sure if this is a bug). 当有足够的可用内存时,自我插入才会给出错误的结果(不确定这是否是错误)。

int main()
{
    int position = 1, first = 2, last = 3;
    // enforce error condition.
    assert(position < first);
    int size = 8;
    // sanity check.
    assert(first < last && last <= size);

    std::vector<int> right, wrong;
    // force resize during insertion.
    right.reserve(size);
    // avoid resize during insertion.
    wrong.reserve(size + (last - first));

    for ( int i = 0; i < size; i++ )
     {
       right.push_back(i);
       wrong.push_back(i);
     }

    std::vector<int>::iterator i;
    i = right.begin();
    right.insert(i + position, i + first, i + last);
    i = wrong.begin();
    wrong.insert(i + position, i + first, i + last);

    assert(right == wrong);
    return 0;
}

Note: The above opinion applies to vector specifically, not containers in general. 注意:以上观点专门适用于vector ,通常不适用于容器。 Also, the suggestion that the above behavior may be a bug has nothing to do with the standard, rather the ease of implementing a robust self insertion for vector . 同样,上述行为可能是错误的建议与标准无关,而是为vector实现强大的自我插入的简便性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM