https://gcc.gnu.org/g:0368c42507328774cadbea589509b95aaf3cb826
commit r15-5942-g0368c42507328774cadbea589509b95aaf3cb826 Author: Jonathan Wakely <jwak...@redhat.com> Date: Thu Dec 5 12:46:26 2024 +0000 libstdc++: Use ADL swap for containers' function objects [PR117921] The standard says that Compare, Pred and Hash objects should be swapped as described in [swappable.requirements] which means calling swap unqualified with std::swap visible to name lookup. libstdc++-v3/ChangeLog: PR libstdc++/117921 * include/bits/hashtable_policy.h (_Hash_code_base::_M_swap): Use ADL swap for Hash members. (_Hashtable_base::_M_swap): Use ADL swap for _Equal members. * include/bits/stl_tree.h (_Rb_tree::swap): Use ADL swap for _Compare members. * testsuite/23_containers/set/modifiers/swap/adl.cc: New test. * testsuite/23_containers/unordered_set/modifiers/swap-2.cc: New test. Diff: --- libstdc++-v3/include/bits/hashtable_policy.h | 8 ++- libstdc++-v3/include/bits/stl_tree.h | 4 +- .../23_containers/set/modifiers/swap/adl.cc | 54 +++++++++++++++++++ .../unordered_set/modifiers/swap-2.cc | 62 ++++++++++++++++++++++ 4 files changed, 125 insertions(+), 3 deletions(-) diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h index ad0dfd55c3f1..f2260f3926dc 100644 --- a/libstdc++-v3/include/bits/hashtable_policy.h +++ b/libstdc++-v3/include/bits/hashtable_policy.h @@ -1177,7 +1177,10 @@ namespace __detail void _M_swap(_Hash_code_base& __x) - { std::swap(__ebo_hash::_M_get(), __x.__ebo_hash::_M_get()); } + { + using std::swap; + swap(__ebo_hash::_M_get(), __x.__ebo_hash::_M_get()); + } const _Hash& _M_hash() const { return __ebo_hash::_M_cget(); } @@ -1561,7 +1564,8 @@ namespace __detail _M_swap(_Hashtable_base& __x) { __hash_code_base::_M_swap(__x); - std::swap(_EqualEBO::_M_get(), __x._EqualEBO::_M_get()); + using std::swap; + swap(_EqualEBO::_M_get(), __x._EqualEBO::_M_get()); } const _Equal& diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h index bc27e191e8b8..0f536517d6b7 100644 --- a/libstdc++-v3/include/bits/stl_tree.h +++ b/libstdc++-v3/include/bits/stl_tree.h @@ -2091,7 +2091,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::swap(this->_M_impl._M_node_count, __t._M_impl._M_node_count); } // No need to swap header's color as it does not change. - std::swap(this->_M_impl._M_key_compare, __t._M_impl._M_key_compare); + + using std::swap; + swap(this->_M_impl._M_key_compare, __t._M_impl._M_key_compare); _Alloc_traits::_S_on_swap(_M_get_Node_allocator(), __t._M_get_Node_allocator()); diff --git a/libstdc++-v3/testsuite/23_containers/set/modifiers/swap/adl.cc b/libstdc++-v3/testsuite/23_containers/set/modifiers/swap/adl.cc new file mode 100644 index 000000000000..2b7975a366fc --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/set/modifiers/swap/adl.cc @@ -0,0 +1,54 @@ +// { dg-do run { target c++11 } } + +// Bug 117921 - containers do not use ADL swap for Compare, Pred or Hash types + +#include <set> +#include <testsuite_hooks.h> + +namespace adl +{ + struct Less : std::less<int> + { + static bool swapped; + friend void swap(Less&, Less&) { swapped = true; } + }; + bool Less::swapped = false; + + struct Allocator_base + { + static bool swapped; + }; + bool Allocator_base::swapped = false; + + using std::size_t; + + template<typename T> + struct Allocator : Allocator_base + { + using value_type = T; + + Allocator() { } + template<typename U> Allocator(const Allocator<U>&) { } + + T* allocate(size_t n) { return std::allocator<T>().allocate(n); } + void deallocate(T* p, size_t n) { std::allocator<T>().deallocate(p, n); } + + using propagate_on_container_swap = std::true_type; + + friend void swap(Allocator&, Allocator&) { swapped = true; } + }; +} + +void +test_swap() +{ + std::set<int, adl::Less, adl::Allocator<int>> s1, s2; + s1.swap(s2); + VERIFY( adl::Less::swapped ); + VERIFY( adl::Allocator_base::swapped ); +} + +int main() +{ + test_swap(); +} diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/swap-2.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/swap-2.cc new file mode 100644 index 000000000000..a0fb1a6f662f --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/swap-2.cc @@ -0,0 +1,62 @@ +// { dg-do run { target c++11 } } + +// Bug 117921 - containers do not use ADL swap for Compare, Pred or Hash types + +#include <unordered_set> +#include <testsuite_hooks.h> + +namespace adl +{ + struct Hash : std::hash<int> + { + static bool swapped; + friend void swap(Hash&, Hash&) { swapped = true; } + }; + bool Hash::swapped = false; + + struct Eq : std::equal_to<int> + { + static bool swapped; + friend void swap(Eq&, Eq&) { swapped = true; } + }; + bool Eq::swapped = false; + + struct Allocator_base + { + static bool swapped; + }; + bool Allocator_base::swapped = false; + + using std::size_t; + + template<typename T> + struct Allocator : Allocator_base + { + using value_type = T; + + Allocator() { } + template<typename U> Allocator(const Allocator<U>&) { } + + T* allocate(size_t n) { return std::allocator<T>().allocate(n); } + void deallocate(T* p, size_t n) { std::allocator<T>().deallocate(p, n); } + + using propagate_on_container_swap = std::true_type; + + friend void swap(Allocator&, Allocator&) { swapped = true; } + }; +} + +void +test_swap() +{ + std::unordered_set<int, adl::Eq, adl::Hash, adl::Allocator<int>> s1, s2; + s1.swap(s2); + VERIFY( adl::Hash::swapped ); + VERIFY( adl::Eq::swapped ); + VERIFY( adl::Allocator_base::swapped ); +} + +int main() +{ + test_swap(); +}