Is it legal to add elements to a preallocated vector in a range-based for loop over that vector?

后端 未结 2 380
灰色年华
灰色年华 2021-01-03 22:30

I\'m using Visual Studio 2015 Update 1 C++ compiler and this code snippet:

#include 
#include 

using namespace std;

int main(         


        
相关标签:
2条回答
  • 2021-01-03 22:42

    Your code exhibits undefined behavior, but it is tricky and tends to only be caught in debug builds.

    When you do a v.push_back, all iterators are invalidated if size passes capacity. You avoid this with a reserve.

    However, even if you don't cause the capacity to grow, the past-the-end iterator is still invalidated. In general, iterator invalidation rules do not distinguish between "the iterator's 'value' would be garbage/refer to a different object" and "the iterator's 'location' is no longer valid". When either occurs, the iterator is simply deemed invalid. As the end iterator is no longer the end iterator (it goes from referring to nothing, to referring to something, in almost every implementation), the standard simply states it is invalidated.

    This code:

    for (auto e: v)
      v.push_back(e*e);
    

    expands to roughly:

    {
      auto && __range = v; 
      for (auto __begin = v.begin(),
        __end = v.end(); 
        __begin != __end;
        ++__begin
      )
      { 
           auto e = *__begin;
           v.push_back(e*e);
      }
    }
    

    the v.push_back call invalidates the __end iterator, which is then compared against, and the debug build correctly flags the undefined behavior as a problem. Debug MSVC iterators are pretty careful with invalidation rules.

    The release build does undefined behavior, and as the vector iterator is basically a thin wrapper around a pointer, and the pointer to the past-the-end element becomes the pointer to the last element after a push back without a capacity overflow, it "works".

    0 讨论(0)
  • 2021-01-03 22:52

    According to documentation:

    If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.

    It says even if capacity is enough past-the-end iterator is invalidated, so I believe your code has UB (unless this documentation is incorrect and standard says otherwise)

    0 讨论(0)
提交回复
热议问题