https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121313
Bug ID: 121313
Summary: vector::insert_range causes self-move-assignment of
elements when given an empty range
Product: gcc
Version: 15.1.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libstdc++
Assignee: unassigned at gcc dot gnu.org
Reporter: gulackeg at gmail dot com
Target Milestone: ---
Test code:
// https://godbolt.org/z/onoax95Ms
#include <cassert>
#include <ranges>
#include <vector>
struct S {
S() = default;
S(S &&) = default;
S(const S &) = default;
S &operator=(S &&other) {
assert(&other != this);
return *this;
}
S &operator=(const S &) = default;
};
int main() {
std::vector<S> v(1);
using namespace std::ranges;
// libc++, MS STL: OK
// libstdc++: Assertion `&other != this' failed
v.insert_range(v.cbegin(), views::empty<S>);
return 0;
}
When given an empty range, libc++'s implementation of insert_range performs
unnecessary self-move-assignments for each element in the vector. This can be
problematic, as self-move-assignment is not guaranteed to be a no-op for
standard library types - it merely leaves the object in a valid but unspecified
state. A notable example is vector itself (its moved-from state is typically
being empty):
// https://godbolt.org/z/av87xTqn7
#include <cassert>
#include <ranges>
#include <vector>
int main() {
std::vector<std::vector<int>> v = {{42}};
assert(v[0].size() == 1);
using namespace std::ranges;
v.insert_range(v.cbegin(), views::empty<std::vector<int>>);
// libc++, MS STL: OK
// libstdc++: Assertion `v[0].size() == 1' failed
assert(v[0].size() == 1);
return 0;
}