Unlike vector::assign_range which requires assignable_from<T&, ranges::range_reference_t<R>> is true, this means that ranges::copy is not necessarily well-formed for vector::insert_range: #include <vector> struct Int { void operator=(int) = delete; Int(int); }; int main() { std::vector<Int> v; v.insert_range(v.begin(), std::vector{42}); } https://godbolt.org/z/jo3vjjo5b
ranges::copy performs exactly the same operations as vector::insert(p, i, j) does, which fails the same way for libstdc++ and libc++: std::vector<Int> v; std::vector<int> vi{42}; v.insert(v.begin(), vi.begin(), vi.end()); So this seems like a pre-existing issue. Arguably, it's a standard defect. Why should be construct new elements when we already have some in the right locations?
MSVC creates a hole in the middle of the vector, destroying the elements that were there previously, then fills the hole using uninitialized_copy.