简体   繁体   中英

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". 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":

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):

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.

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:

[...] pre: i and j are not iterators into 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. 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.

This is safe because v.begin() and v.end() are always current.

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. 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;
}

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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