Reputation: 3402
Is it safe to insert a range in std::vector
if its source is the tail of the same vector?
I need to insert some elements to std::vector
as copies of elements in the same vector which positioned later than the inserted place.
So, I wrote the following code demo :
#include <iostream>
#include <ranges>
#include <vector>
int main()
{
{
std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
v.reserve(2*v.size());
// Works just fine on both CLang and MSVC
v.insert(
v.begin() + 3,
v.begin() + 5,
v.end()
);
std::cout << "insert: ";
std::ranges::copy(v, std::ostream_iterator<int>{std::cout, ", "});
std::cout << std::endl;
}
{
std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto range_to_insert = std::ranges::subrange(v.begin() + 5, v.end());
v.reserve(v.size() + range_to_insert.size()); // To avoid reallocation and iterators invalidation during insert_range
// If previous reservation took place, crashes address sanitizer on MSVC and gives wrong results in Debug on my local PC with the latest MSVC x64
v.insert_range(
v.begin() + 3,
range_to_insert
);
std::cout << "insert_range: ";
std::ranges::copy(v, std::ostream_iterator<int>{std::cout, ", "});
std::cout << std::endl;
}
}
I can’t reproduce this on godbolt, but on my local PC with the latest MSVC x64 in Debug it shows:
insert: 0, 1, 2, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9,
insert_range: 0, 1, 2, -572662307, -572662307, -572662307, -572662307, -572662307, 3, 4, 5, 6, 7, 8, 9,
while in Release it shows the same result as on godbolt:
insert: 0, 1, 2, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9,
insert_range: 0, 1, 2, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9,
In all cases address sanitized at _Copy_memmove
with long to show error log. And this crashes if and only if I use this line:
v.reserve(v.size() + range_to_insert.size());
I expected to have quite opposite behavior, and introduced it to avoid reallocation during insert_range
. In case I remove this line, code works fine both in Release and Debug.
I can’t see any limitations for such insert neither for std::vector<T,Allocator>::insert_range, nor for std::vector<T,Allocator>::insert.
The question is, are such operations safe or there is some violation? Can I insert elements, copying from the tail of the vector (which could affect iterators somehow) and can I insert elements, if indexes of inserted elements overlap with old indexes of source elements?
I do understand that some things in the question above are unrelated with ranges insert operations where both indexes and iterators involved, but I hope you get the point correctly.
Upvotes: 0
Views: 64