Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterator assignment in a condition - vector iterators incompatible

I have a wrapper for std::vector and I've implemented function that replaces a section within a vector with another vector. I've tried to put the assignment of an iterator directly in the if condition and got unexpected results.

I'm using Visual Studio 2013 and with FAIL defined I get Debug Assertion Failed! - vector iterators incompatible. Is it possible that the condition is being evaluated from right to left? I can't get over it.

This is a (poorly implemented) code that reproduces my problem - meant to replace 3rd and 4th elements of vec with 1st and 2nd elements of vec_second:

#include <iostream>
#include <vector>
#include <iterator>
using std::cout;

//#define FAIL

int main()
{
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::vector<int> vec_second = {6, 7};

    auto it = vec.begin() + 2;

#ifndef FAIL
    it = vec.erase(it, it + vec_second.size());

    if(it == vec.end())
#else
    if((it = vec.erase(it, it + vec_second.size())) == vec.end())
#endif
        vec.reserve(vec.size() + vec_second.size());

    vec.insert(it, vec_second.begin(), vec_second.end());

    for(auto const& x : vec)
        cout << x << " ";
}

But runs fine on Coliru's GCC.

like image 867
LogicStuff Avatar asked Mar 17 '23 04:03

LogicStuff


1 Answers

if((it = vec.erase(it, it + vec_second.size())) == vec.end())

Since there is no sequence point between them, a compiler is free to call erase and end in either order. If end gets called first, that iterator is immediately invalidated by erase, leading to undefined behavior.

Your #ifndef FAIL code is the safe way to do this.

like image 148
aschepler Avatar answered Apr 16 '23 14:04

aschepler