https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87749
Bug ID: 87749 Summary: [6/7/8/9 Regression] std::__cxx11::basic_string move assignment does deep copy unnecessarily Product: gcc Version: 9.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: redi at gcc dot gnu.org Target Milestone: --- #include <string> bool oom = false; template<typename T> struct alloc { using value_type = T; using size_type = unsigned long; using difference_type = long; using reference = T&; using const_reference = T&; using pointer = T*; using const_pointer = const T*; int not_empty = 0; template<typename U> struct rebind { using other = alloc<U>; }; alloc() = default; template<typename U> alloc(const alloc<U>&) { } T* allocate(unsigned long n) { if (oom) throw std::bad_alloc(); return std::allocator<T>().allocate(n); } void deallocate(T* p, unsigned long n) { std::allocator<T>().deallocate(p, n); } }; template<typename T, typename U> bool operator==(const alloc<T>&, const alloc<U>&) { return true; } template<typename T, typename U> bool operator!=(const alloc<T>&, const alloc<U>&) { return false; } int main() { using string = std::basic_string<char, std::char_traits<char>, alloc<char>>; string s = "a string that is longer than a short string"; oom = true; string ss; ss = std::move(s); } The move assignment should transfer the allocated storage from s to ss, but instead it does a deep copy, which throws here when an out-of-memory condition is simulated. The problem is that the move assignment operator only checks _S_always_equal and if it's false doesn't do a runtime check for allocator equality. I'm marking this as a regression, because prior to GCC 5 this code would not fail when compiled with no options except -std=c++11. Since the default string changed to the cxx11 one it fails (but still passes if the COW string is used).