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).