https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110945
--- Comment #11 from Jonathan Wakely <redi at gcc dot gnu.org> --- (In reply to Jonathan Wakely from comment #6) > And _M_replace_dispatch creates a new copy anyway: > > _M_replace_dispatch(const_iterator __i1, const_iterator __i2, > _InputIterator __k1, _InputIterator __k2, > std::__false_type) > { > // _GLIBCXX_RESOLVE_LIB_DEFECTS > // 2788. unintentionally require a default constructible allocator > const basic_string __s(__k1, __k2, this->get_allocator()); > const size_type __n1 = __i2 - __i1; > return _M_replace(__i1 - begin(), __n1, __s._M_data(), > __s.size()); > } When distance(k1, k2) > this->capacity() this function will make two copies of [k1,k2) and allocate twice. So even with the checks for disjunct strings, we do a lot more work than the copy construction benchmarks. With this change we make a single allocation+copy and then do a cheap move assignment: --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -1711,4 +1711,4 @@ basic_string& assign(_InputIterator __first, _InputIterator __last) - { return this->replace(begin(), end(), __first, __last); } + { return *this = basic_string(__first, __last, get_allocator()); }