diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2024-09-10 14:25:41 +0100 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2024-09-10 18:22:32 +0100 |
commit | c07cf418fdde0c192e370a8d76a991cc7215e9c4 (patch) | |
tree | cfc6067cedb0b43f234cbdc21a9c3e17b15e7c7e | |
parent | 0008050b9d6046ba4e811a03b406fb5d98707cae (diff) | |
download | gcc-c07cf418fdde0c192e370a8d76a991cc7215e9c4.zip gcc-c07cf418fdde0c192e370a8d76a991cc7215e9c4.tar.gz gcc-c07cf418fdde0c192e370a8d76a991cc7215e9c4.tar.bz2 |
libstdc++: std::string move assignment should not use POCCA trait [PR116641]
The changes to implement LWG 2579 (r10-327-gdb33efde17932f) made
std::string::assign use the propagate_on_container_copy_assignment
(POCCA) trait, for consistency with operator=(const basic_string&).
However, this also unintentionally affected operator=(basic_string&&)
which calls assign(str) to make a deep copy when performing a move is
not possible. The fix is for the move assignment operator to call
_M_assign(str) instead of assign(str), as this just does the deep copy
and doesn't check the POCCA trait first.
The bug only affects the unlikely/useless combination of POCCA==true and
POCMA==false, but we should fix it for correctness anyway. it should
also make move assignment slightly cheaper to compile and execute,
because we skip the extra code in assign(const basic_string&).
libstdc++-v3/ChangeLog:
PR libstdc++/116641
* include/bits/basic_string.h (operator=(basic_string&&)): Call
_M_assign instead of assign.
* testsuite/21_strings/basic_string/allocator/116641.cc: New
test.
-rw-r--r-- | libstdc++-v3/include/bits/basic_string.h | 2 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/21_strings/basic_string/allocator/116641.cc | 53 |
2 files changed, 54 insertions, 1 deletions
diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h index 944bd23..120c0bc 100644 --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -915,7 +915,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 __str._M_data(__str._M_use_local_data()); } else // Need to do a deep copy - assign(__str); + _M_assign(__str); __str.clear(); return *this; } diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/116641.cc b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/116641.cc new file mode 100644 index 0000000..a1a411b --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/116641.cc @@ -0,0 +1,53 @@ +// { dg-do run { target c++11 } } +// { dg-require-effective-target cxx11_abi } + +// Bug 116641 - std::string move assignment incorrectly depends on POCCA + +#include <string> +#include <testsuite_hooks.h> + +template<typename T> +struct Alloc +{ + using value_type = T; + using propagate_on_container_swap = std::false_type; + using propagate_on_container_copy_assignment = std::true_type; + using propagate_on_container_move_assignment = std::false_type; + + Alloc(int id) : id(id) { } + + template<typename U> + Alloc(const Alloc<U>& a) : id(a.id) { } + + T* allocate(unsigned long n) + { return std::allocator<T>().allocate(n); } + + void deallocate(T* p, unsigned long n) + { std::allocator<T>().deallocate(p, n); } + + Alloc& operator=(const Alloc&) { throw; } + + bool operator==(const Alloc& a) const { return id == a.id; } + bool operator!=(const Alloc& a) const { return id != a.id; } + + int id; +}; + +void +test_pr116641() +{ + Alloc<char> a1(1), a2(2); + std::basic_string<char, std::char_traits<char>, Alloc<char>> s1(a1), s2(a2); + + s1 = "allocator should not propagate on move assignment"; + VERIFY( s1.get_allocator() == a1 ); + VERIFY( s2.get_allocator() == a2 ); + s2 = std::move(s1); + VERIFY( s1.get_allocator() == a1 ); + VERIFY( s2.get_allocator() == a2 ); +} + +int main() +{ + test_pr116641(); +} |