diff options
author | François Dumont <fdumont@gcc.gnu.org> | 2023-01-04 19:34:21 +0100 |
---|---|---|
committer | François Dumont <fdumont@gcc.gnu.org> | 2023-02-04 14:03:54 +0100 |
commit | 540a22d243966d1b882db26b17fe674467e2a169 (patch) | |
tree | 3d5616980078acbf6d95ed2f778b91d566d6bb87 /libstdc++-v3 | |
parent | 49e52115b09b477382fef6f04fd7b4d1641f902c (diff) | |
download | gcc-540a22d243966d1b882db26b17fe674467e2a169.zip gcc-540a22d243966d1b882db26b17fe674467e2a169.tar.gz gcc-540a22d243966d1b882db26b17fe674467e2a169.tar.bz2 |
libstdc++: Optimize basic_string move assignment
Since resolution of Issue 2593 [1] we can consider that equal allocators
before the propagate-on-move-assignment operations will still be equal
afterward.
So we can extend the optimization of transfering the storage of the move-to
instance to the move-from one that is currently limited to always equal
allocators.
[1] https://cplusplus.github.io/LWG/issue2593
libstdc++-v3/ChangeLog:
* include/bits/basic_string.h (operator=(basic_string&&)): Transfer move-to
storage to the move-from instance when allocators are equal.
* testsuite/21_strings/basic_string/allocator/char/move_assign.cc (test04):
New test case.
Diffstat (limited to 'libstdc++-v3')
-rw-r--r-- | libstdc++-v3/include/bits/basic_string.h | 11 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/move_assign.cc | 52 |
2 files changed, 57 insertions, 6 deletions
diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h index aa01826..c81dc0d 100644 --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -844,9 +844,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 operator=(basic_string&& __str) noexcept(_Alloc_traits::_S_nothrow_move()) { + const bool __equal_allocs = _Alloc_traits::_S_always_equal() + || _M_get_allocator() == __str._M_get_allocator(); if (!_M_is_local() && _Alloc_traits::_S_propagate_on_move_assign() - && !_Alloc_traits::_S_always_equal() - && _M_get_allocator() != __str._M_get_allocator()) + && !__equal_allocs) { // Destroy existing storage before replacing allocator. _M_destroy(_M_allocated_capacity); @@ -868,16 +869,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _M_set_length(__str.size()); } } - else if (_Alloc_traits::_S_propagate_on_move_assign() - || _Alloc_traits::_S_always_equal() - || _M_get_allocator() == __str._M_get_allocator()) + else if (_Alloc_traits::_S_propagate_on_move_assign() || __equal_allocs) { // Just move the allocated pointer, our allocator can free it. pointer __data = nullptr; size_type __capacity; if (!_M_is_local()) { - if (_Alloc_traits::_S_always_equal()) + if (__equal_allocs) { // __str can reuse our existing storage. __data = _M_data(); diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/move_assign.cc b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/move_assign.cc index cc58348e..21e0b1c 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/move_assign.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/move_assign.cc @@ -28,6 +28,8 @@ const C c = 'a'; using traits = std::char_traits<C>; using __gnu_test::propagating_allocator; +using __gnu_test::tracker_allocator_counter; +using __gnu_test::tracker_allocator; void test01() { @@ -146,10 +148,60 @@ void test03() VERIFY(7 == v8.get_allocator().get_personality()); } +void test04() +{ + typedef propagating_allocator<C, true, tracker_allocator<C>> alloc_type; + typedef std::basic_string<C, traits, alloc_type> test_type; + + { + tracker_allocator_counter::reset(); + test_type v1(alloc_type(1)); + v1 = "abcdefghijklmnopqr10"; + auto ref_alloc_count = tracker_allocator_counter::get_allocation_count(); + + test_type v2(alloc_type(2)); + v2 = "abcdefghijklmnopqr20"; + v2 = std::move(v1); + VERIFY(1 == v1.get_allocator().get_personality()); + VERIFY(1 == v2.get_allocator().get_personality()); + + VERIFY( tracker_allocator_counter::get_allocation_count() == 2 * ref_alloc_count ); + VERIFY( tracker_allocator_counter::get_deallocation_count() == ref_alloc_count ); + + v1 = "abcdefghijklmnopqr11"; + + VERIFY( tracker_allocator_counter::get_allocation_count() == 3 * ref_alloc_count ); + } + + { + tracker_allocator_counter::reset(); + test_type v1(alloc_type(1)); + v1 = "abcdefghijklmnopqr10"; + auto ref_alloc_count = tracker_allocator_counter::get_allocation_count(); + + test_type v2(alloc_type(1)); + v2 = "abcdefghijklmnopqr20"; + v2 = std::move(v1); + VERIFY(1 == v1.get_allocator().get_personality()); + VERIFY(1 == v2.get_allocator().get_personality()); + + VERIFY( tracker_allocator_counter::get_allocation_count() == 2 * ref_alloc_count ); + VERIFY( tracker_allocator_counter::get_deallocation_count() == 0 ); + + v1 = "abcdefghijklmnopqr11"; + + VERIFY( tracker_allocator_counter::get_allocation_count() == 2 * ref_alloc_count ); + } + + VERIFY( tracker_allocator_counter::get_allocation_count() == + tracker_allocator_counter::get_deallocation_count() ); +} + int main() { test01(); test02(); test03(); + test04(); return 0; } |