[libcxx-commits] [libcxx] [libc++] Optimize __hash_table copy constructors and assignment (PR #151951)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Aug 4 05:25:45 PDT 2025
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/151951
None
>From f971425747ef712bb19da065d42791fd657cf1fa Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 2 Aug 2025 08:06:41 +0200
Subject: [PATCH] [libc++] Optimize __hash_table copy constructors and
assignment
---
libcxx/include/__hash_table | 131 +++++++++--
libcxx/include/unordered_map | 68 +-----
libcxx/include/unordered_set | 47 +---
.../unord.map/unord.map.cnstr/copy.pass.cpp | 215 ++++++++----------
.../unord.map.cnstr/copy_alloc.pass.cpp | 180 ++++++---------
5 files changed, 299 insertions(+), 342 deletions(-)
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index dacc152030e14..e25d113dcc2b7 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -10,6 +10,7 @@
#ifndef _LIBCPP___HASH_TABLE
#define _LIBCPP___HASH_TABLE
+#include <__algorithm/fill_n.h>
#include <__algorithm/max.h>
#include <__algorithm/min.h>
#include <__assert>
@@ -700,6 +701,38 @@ private:
_LIBCPP_HIDE_FROM_ABI size_type& size() _NOEXCEPT { return __size_; }
+ _LIBCPP_HIDE_FROM_ABI void
+ __copy_construct(__next_pointer __other_iter, __next_pointer __own_iter, size_t __current_chash) {
+ auto __bucket_count = bucket_count();
+
+ for (; __other_iter; __other_iter = __other_iter->__next_) {
+ __node_holder __new_node = __construct_node_hash(__other_iter->__hash(), __other_iter->__upcast()->__get_value());
+
+ size_t __new_chash = std::__constrain_hash(__new_node->__hash(), __bucket_count);
+ if (__new_chash != __current_chash) {
+ __bucket_list_[__new_chash] = __own_iter;
+ __current_chash = __new_chash;
+ }
+
+ __own_iter->__next_ = static_cast<__next_pointer>(__new_node.release());
+ __own_iter = __own_iter->__next_;
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void __copy_construct(__next_pointer __other_iter) {
+ __next_pointer __own_iter = __first_node_.__ptr();
+ {
+ __node_holder __new_node = __construct_node_hash(__other_iter->__hash(), __other_iter->__upcast()->__get_value());
+ __own_iter->__next_ = static_cast<__next_pointer>(__new_node.release());
+ }
+
+ size_t __current_chash = std::__constrain_hash(__own_iter->__next_->__hash(), bucket_count());
+ __bucket_list_[__current_chash] = __own_iter;
+ __other_iter = __other_iter->__next_;
+ __own_iter = __own_iter->__next_;
+ __copy_construct(__other_iter, __own_iter, __current_chash);
+ }
+
public:
_LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __size_; }
@@ -1048,16 +1081,29 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__hash_table(const allocator_type& __a
__max_load_factor_(1.0f) {}
template <class _Tp, class _Hash, class _Equal, class _Alloc>
-__hash_table<_Tp, _Hash, _Equal, _Alloc>::__hash_table(const __hash_table& __u)
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::__hash_table(const __hash_table& __other)
: __bucket_list_(nullptr,
- __bucket_list_deleter(allocator_traits<__pointer_allocator>::select_on_container_copy_construction(
- __u.__bucket_list_.get_deleter().__alloc()),
+ __bucket_list_deleter(__pointer_alloc_traits::select_on_container_copy_construction(
+ __other.__bucket_list_.get_deleter().__alloc()),
0)),
- __node_alloc_(allocator_traits<__node_allocator>::select_on_container_copy_construction(__u.__node_alloc())),
+ __node_alloc_(__node_traits::select_on_container_copy_construction(__other.__node_alloc())),
__size_(0),
- __hasher_(__u.hash_function()),
- __max_load_factor_(__u.__max_load_factor_),
- __key_eq_(__u.__key_eq_) {}
+ __hasher_(__other.hash_function()),
+ __max_load_factor_(__other.__max_load_factor_),
+ __key_eq_(__other.__key_eq_) {
+ if (__other.size() == 0)
+ return;
+
+ auto& __bucket_list_del = __bucket_list_.get_deleter();
+ auto __bucket_count = __other.bucket_count();
+ __bucket_list_.reset(__pointer_alloc_traits::allocate(__bucket_list_del.__alloc(), __bucket_count));
+ __bucket_list_del.size() = __bucket_count;
+
+ std::fill_n(__bucket_list_.get(), __bucket_count, nullptr);
+
+ __copy_construct(__other.__first_node_.__next_);
+ __size_ = __other.size();
+}
template <class _Tp, class _Hash, class _Equal, class _Alloc>
__hash_table<_Tp, _Hash, _Equal, _Alloc>::__hash_table(const __hash_table& __u, const allocator_type& __a)
@@ -1131,14 +1177,71 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__copy_assign_alloc(const __hash_
}
template <class _Tp, class _Hash, class _Equal, class _Alloc>
-__hash_table<_Tp, _Hash, _Equal, _Alloc>& __hash_table<_Tp, _Hash, _Equal, _Alloc>::operator=(const __hash_table& __u) {
- if (this != std::addressof(__u)) {
- __copy_assign_alloc(__u);
- hash_function() = __u.hash_function();
- key_eq() = __u.key_eq();
- max_load_factor() = __u.max_load_factor();
- __assign_multi(__u.begin(), __u.end());
+__hash_table<_Tp, _Hash, _Equal, _Alloc>&
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::operator=(const __hash_table& __other) {
+ if (this == std::addressof(__other))
+ return *this;
+
+ __copy_assign_alloc(__other);
+ hash_function() = __other.hash_function();
+ key_eq() = __other.key_eq();
+ max_load_factor() = __other.max_load_factor();
+
+ if (__other.size() == 0) {
+ clear();
+ return *this;
+ }
+
+ auto __bucket_count = __other.bucket_count();
+ if (__bucket_count != bucket_count()) {
+ auto& __bucket_list_del = __bucket_list_.get_deleter();
+ __bucket_list_.reset(__pointer_alloc_traits::allocate(__bucket_list_del.__alloc(), __bucket_count));
+ __bucket_list_del.size() = __bucket_count;
+ }
+ std::fill_n(__bucket_list_.get(), __bucket_count, nullptr);
+
+ if (!__first_node_.__next_) {
+ __copy_construct(__other.__first_node_.__next_);
+ __size_ = __other.size();
+ return *this;
}
+
+ __next_pointer __other_iter = __other.__first_node_.__next_;
+ __next_pointer __own_iter = __first_node_.__ptr();
+ {
+ __node_pointer __next = __own_iter->__next_->__upcast();
+ __assign_value(__next->__get_value(), __other_iter->__upcast()->__get_value());
+ __next->__hash_ = __other_iter->__hash();
+ }
+ size_t __current_chash = std::__constrain_hash(__own_iter->__next_->__hash(), __bucket_count);
+ __bucket_list_[__current_chash] = __own_iter;
+ __other_iter = __other_iter->__next_;
+ __own_iter = __own_iter->__next_;
+
+ while (__other_iter && __own_iter->__next_) {
+ __node_pointer __next = __own_iter->__next_->__upcast();
+ __assign_value(__next->__get_value(), __other_iter->__upcast()->__get_value());
+ __next->__hash_ = __other_iter->__hash();
+
+ size_t __new_chash = std::__constrain_hash(__next->__hash_, __bucket_count);
+ if (__new_chash != __current_chash) {
+ __bucket_list_[__new_chash] = __own_iter;
+ __current_chash = __new_chash;
+ }
+
+ __other_iter = __other_iter->__next_;
+ __own_iter = __own_iter->__next_;
+ }
+
+ if (__own_iter->__next_) {
+ __deallocate_node(__own_iter->__next_);
+ __own_iter->__next_ = nullptr;
+ } else {
+ __copy_construct(__other_iter, __own_iter, __current_chash);
+ }
+
+ __size_ = __other.size();
+
return *this;
}
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 97c2c52eba337..009dfb7a1e2b7 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1046,10 +1046,11 @@ public:
# endif
_LIBCPP_HIDE_FROM_ABI explicit unordered_map(const allocator_type& __a);
- _LIBCPP_HIDE_FROM_ABI unordered_map(const unordered_map& __u);
+ _LIBCPP_HIDE_FROM_ABI unordered_map(const unordered_map& __u) = default;
_LIBCPP_HIDE_FROM_ABI unordered_map(const unordered_map& __u, const allocator_type& __a);
# ifndef _LIBCPP_CXX03_LANG
- _LIBCPP_HIDE_FROM_ABI unordered_map(unordered_map&& __u) _NOEXCEPT_(is_nothrow_move_constructible<__table>::value);
+ _LIBCPP_HIDE_FROM_ABI unordered_map(unordered_map&& __u)
+ _NOEXCEPT_(is_nothrow_move_constructible<__table>::value) = default;
_LIBCPP_HIDE_FROM_ABI unordered_map(unordered_map&& __u, const allocator_type& __a);
_LIBCPP_HIDE_FROM_ABI unordered_map(initializer_list<value_type> __il);
_LIBCPP_HIDE_FROM_ABI
@@ -1099,24 +1100,10 @@ public:
static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Key, _Hash, _Pred>(0)), "");
}
- _LIBCPP_HIDE_FROM_ABI unordered_map& operator=(const unordered_map& __u) {
-# ifndef _LIBCPP_CXX03_LANG
- __table_ = __u.__table_;
-# else
- if (this != std::addressof(__u)) {
- __table_.clear();
- __table_.hash_function() = __u.__table_.hash_function();
- __table_.key_eq() = __u.__table_.key_eq();
- __table_.max_load_factor() = __u.__table_.max_load_factor();
- __table_.__copy_assign_alloc(__u.__table_);
- insert(__u.begin(), __u.end());
- }
-# endif
- return *this;
- }
+ _LIBCPP_HIDE_FROM_ABI unordered_map& operator=(const unordered_map& __u) = default;
# ifndef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI unordered_map& operator=(unordered_map&& __u)
- _NOEXCEPT_(is_nothrow_move_assignable<__table>::value);
+ _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) = default;
_LIBCPP_HIDE_FROM_ABI unordered_map& operator=(initializer_list<value_type> __il);
# endif // _LIBCPP_CXX03_LANG
@@ -1563,12 +1550,6 @@ unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(
insert(__first, __last);
}
-template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
-unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(const unordered_map& __u) : __table_(__u.__table_) {
- __table_.__rehash_unique(__u.bucket_count());
- insert(__u.begin(), __u.end());
-}
-
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(const unordered_map& __u, const allocator_type& __a)
: __table_(__u.__table_, typename __table::allocator_type(__a)) {
@@ -1578,11 +1559,6 @@ unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(const unordered_ma
# ifndef _LIBCPP_CXX03_LANG
-template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
-inline unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(unordered_map&& __u)
- _NOEXCEPT_(is_nothrow_move_constructible<__table>::value)
- : __table_(std::move(__u.__table_)) {}
-
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(unordered_map&& __u, const allocator_type& __a)
: __table_(std::move(__u.__table_), typename __table::allocator_type(__a)) {
@@ -1618,14 +1594,6 @@ unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(
insert(__il.begin(), __il.end());
}
-template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
-inline unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>&
-unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator=(unordered_map&& __u)
- _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) {
- __table_ = std::move(__u.__table_);
- return *this;
-}
-
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
inline unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>&
unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator=(initializer_list<value_type> __il) {
@@ -1852,11 +1820,11 @@ public:
# endif
_LIBCPP_HIDE_FROM_ABI explicit unordered_multimap(const allocator_type& __a);
- _LIBCPP_HIDE_FROM_ABI unordered_multimap(const unordered_multimap& __u);
+ _LIBCPP_HIDE_FROM_ABI unordered_multimap(const unordered_multimap& __u) = default;
_LIBCPP_HIDE_FROM_ABI unordered_multimap(const unordered_multimap& __u, const allocator_type& __a);
# ifndef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI unordered_multimap(unordered_multimap&& __u)
- _NOEXCEPT_(is_nothrow_move_constructible<__table>::value);
+ _NOEXCEPT_(is_nothrow_move_constructible<__table>::value) = default;
_LIBCPP_HIDE_FROM_ABI unordered_multimap(unordered_multimap&& __u, const allocator_type& __a);
_LIBCPP_HIDE_FROM_ABI unordered_multimap(initializer_list<value_type> __il);
_LIBCPP_HIDE_FROM_ABI unordered_multimap(
@@ -1923,7 +1891,7 @@ public:
}
# ifndef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI unordered_multimap& operator=(unordered_multimap&& __u)
- _NOEXCEPT_(is_nothrow_move_assignable<__table>::value);
+ _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) = default;
_LIBCPP_HIDE_FROM_ABI unordered_multimap& operator=(initializer_list<value_type> __il);
# endif // _LIBCPP_CXX03_LANG
@@ -2315,13 +2283,6 @@ template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
inline unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(const allocator_type& __a)
: __table_(typename __table::allocator_type(__a)) {}
-template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
-unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(const unordered_multimap& __u)
- : __table_(__u.__table_) {
- __table_.__rehash_multi(__u.bucket_count());
- insert(__u.begin(), __u.end());
-}
-
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(
const unordered_multimap& __u, const allocator_type& __a)
@@ -2332,11 +2293,6 @@ unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(
# ifndef _LIBCPP_CXX03_LANG
-template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
-inline unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(unordered_multimap&& __u)
- _NOEXCEPT_(is_nothrow_move_constructible<__table>::value)
- : __table_(std::move(__u.__table_)) {}
-
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(
unordered_multimap&& __u, const allocator_type& __a)
@@ -2373,14 +2329,6 @@ unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(
insert(__il.begin(), __il.end());
}
-template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
-inline unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>&
-unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::operator=(unordered_multimap&& __u)
- _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) {
- __table_ = std::move(__u.__table_);
- return *this;
-}
-
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
inline unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>&
unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::operator=(initializer_list<value_type> __il) {
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 475715db62bdb..09bd81a22eae5 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -703,7 +703,7 @@ public:
# endif
_LIBCPP_HIDE_FROM_ABI explicit unordered_set(const allocator_type& __a);
- _LIBCPP_HIDE_FROM_ABI unordered_set(const unordered_set& __u);
+ _LIBCPP_HIDE_FROM_ABI unordered_set(const unordered_set& __u) = default;
_LIBCPP_HIDE_FROM_ABI unordered_set(const unordered_set& __u, const allocator_type& __a);
# ifndef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI unordered_set(unordered_set&& __u) _NOEXCEPT_(is_nothrow_move_constructible<__table>::value);
@@ -733,13 +733,10 @@ public:
static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), "");
}
- _LIBCPP_HIDE_FROM_ABI unordered_set& operator=(const unordered_set& __u) {
- __table_ = __u.__table_;
- return *this;
- }
+ _LIBCPP_HIDE_FROM_ABI unordered_set& operator=(const unordered_set& __u) = default;
# ifndef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI unordered_set& operator=(unordered_set&& __u)
- _NOEXCEPT_(is_nothrow_move_assignable<__table>::value);
+ _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) = default;
_LIBCPP_HIDE_FROM_ABI unordered_set& operator=(initializer_list<value_type> __il);
# endif // _LIBCPP_CXX03_LANG
@@ -1070,12 +1067,6 @@ unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(
template <class _Value, class _Hash, class _Pred, class _Alloc>
inline unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(const allocator_type& __a) : __table_(__a) {}
-template <class _Value, class _Hash, class _Pred, class _Alloc>
-unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(const unordered_set& __u) : __table_(__u.__table_) {
- __table_.__rehash_unique(__u.bucket_count());
- insert(__u.begin(), __u.end());
-}
-
template <class _Value, class _Hash, class _Pred, class _Alloc>
unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(const unordered_set& __u, const allocator_type& __a)
: __table_(__u.__table_, __a) {
@@ -1125,14 +1116,6 @@ unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(
insert(__il.begin(), __il.end());
}
-template <class _Value, class _Hash, class _Pred, class _Alloc>
-inline unordered_set<_Value, _Hash, _Pred, _Alloc>&
-unordered_set<_Value, _Hash, _Pred, _Alloc>::operator=(unordered_set&& __u)
- _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) {
- __table_ = std::move(__u.__table_);
- return *this;
-}
-
template <class _Value, class _Hash, class _Pred, class _Alloc>
inline unordered_set<_Value, _Hash, _Pred, _Alloc>&
unordered_set<_Value, _Hash, _Pred, _Alloc>::operator=(initializer_list<value_type> __il) {
@@ -1308,7 +1291,7 @@ public:
# endif
_LIBCPP_HIDE_FROM_ABI explicit unordered_multiset(const allocator_type& __a);
- _LIBCPP_HIDE_FROM_ABI unordered_multiset(const unordered_multiset& __u);
+ _LIBCPP_HIDE_FROM_ABI unordered_multiset(const unordered_multiset& __u) = default;
_LIBCPP_HIDE_FROM_ABI unordered_multiset(const unordered_multiset& __u, const allocator_type& __a);
# ifndef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI unordered_multiset(unordered_multiset&& __u)
@@ -1339,13 +1322,10 @@ public:
static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), "");
}
- _LIBCPP_HIDE_FROM_ABI unordered_multiset& operator=(const unordered_multiset& __u) {
- __table_ = __u.__table_;
- return *this;
- }
+ _LIBCPP_HIDE_FROM_ABI unordered_multiset& operator=(const unordered_multiset& __u) = default;
# ifndef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI unordered_multiset& operator=(unordered_multiset&& __u)
- _NOEXCEPT_(is_nothrow_move_assignable<__table>::value);
+ _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) = default;
_LIBCPP_HIDE_FROM_ABI unordered_multiset& operator=(initializer_list<value_type> __il);
# endif // _LIBCPP_CXX03_LANG
@@ -1685,13 +1665,6 @@ template <class _Value, class _Hash, class _Pred, class _Alloc>
inline unordered_multiset<_Value, _Hash, _Pred, _Alloc>::unordered_multiset(const allocator_type& __a)
: __table_(__a) {}
-template <class _Value, class _Hash, class _Pred, class _Alloc>
-unordered_multiset<_Value, _Hash, _Pred, _Alloc>::unordered_multiset(const unordered_multiset& __u)
- : __table_(__u.__table_) {
- __table_.__rehash_multi(__u.bucket_count());
- insert(__u.begin(), __u.end());
-}
-
template <class _Value, class _Hash, class _Pred, class _Alloc>
unordered_multiset<_Value, _Hash, _Pred, _Alloc>::unordered_multiset(
const unordered_multiset& __u, const allocator_type& __a)
@@ -1743,14 +1716,6 @@ unordered_multiset<_Value, _Hash, _Pred, _Alloc>::unordered_multiset(
insert(__il.begin(), __il.end());
}
-template <class _Value, class _Hash, class _Pred, class _Alloc>
-inline unordered_multiset<_Value, _Hash, _Pred, _Alloc>&
-unordered_multiset<_Value, _Hash, _Pred, _Alloc>::operator=(unordered_multiset&& __u)
- _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) {
- __table_ = std::move(__u.__table_);
- return *this;
-}
-
template <class _Value, class _Hash, class _Pred, class _Alloc>
inline unordered_multiset<_Value, _Hash, _Pred, _Alloc>&
unordered_multiset<_Value, _Hash, _Pred, _Alloc>::operator=(initializer_list<value_type> __il) {
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy.pass.cpp
index 793807babbdca..ec233afb294d9 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy.pass.cpp
@@ -14,136 +14,115 @@
// unordered_map(const unordered_map& u);
-#include <unordered_map>
-#include <string>
#include <cassert>
#include <cfloat>
#include <cmath>
#include <cstddef>
+#include <unordered_map>
-#include "test_macros.h"
#include "../../../test_compare.h"
#include "../../../test_hash.h"
-#include "test_allocator.h"
#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
-int main(int, char**) {
- {
- typedef std::unordered_map<int,
- std::string,
- test_hash<int>,
- test_equal_to<int>,
- test_allocator<std::pair<const int, std::string> > >
- C;
- typedef std::pair<int, std::string> P;
- P a[] = {
- P(1, "one"),
- P(2, "two"),
- P(3, "three"),
- P(4, "four"),
- P(1, "four"),
- P(2, "four"),
- };
- C c0(a,
- a + sizeof(a) / sizeof(a[0]),
- 7,
- test_hash<int>(8),
- test_equal_to<int>(9),
- test_allocator<std::pair<const int, std::string> >(10));
- C c = c0;
- LIBCPP_ASSERT(c.bucket_count() == 7);
- assert(c.size() == 4);
- assert(c.at(1) == "one");
- assert(c.at(2) == "two");
- assert(c.at(3) == "three");
- assert(c.at(4) == "four");
- assert(c.hash_function() == test_hash<int>(8));
- assert(c.key_eq() == test_equal_to<int>(9));
- assert(c.get_allocator() == (test_allocator<std::pair<const int, std::string> >(10)));
- assert(!c.empty());
- assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
- assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
- assert(std::fabs(c.load_factor() - (float)c.size() / c.bucket_count()) < FLT_EPSILON);
- assert(c.max_load_factor() == 1);
+template <class Alloc>
+void test_alloc() {
+ { // Simple check
+ using V = std::pair<const int, int>;
+ using Map = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, Alloc>;
+
+ V arr[] = {V(1, 2), V(2, 4), V(3, 1)};
+ const Map orig(std::begin(arr), std::end(arr));
+ Map copy = orig;
+ LIBCPP_ASSERT(copy.bucket_count() == 5);
+ assert(copy.size() == 3);
+ assert(copy.at(1) == 2);
+ assert(copy.at(2) == 4);
+ assert(copy.at(3) == 1);
+ assert(static_cast<std::size_t>(std::distance(copy.begin(), copy.end())) == copy.size());
+ assert(std::fabs(copy.load_factor() - static_cast<float>(copy.size()) / copy.bucket_count()) < FLT_EPSILON);
+ assert(copy.max_load_factor() == 1.f);
+
+ // Check that orig is still what is expected
+ LIBCPP_ASSERT(orig.bucket_count() == 5);
+ assert(orig.size() == 3);
+ assert(orig.at(1) == 2);
+ assert(orig.at(2) == 4);
+ assert(orig.at(3) == 1);
}
-#if TEST_STD_VER >= 11
- {
- typedef std::unordered_map<int,
- std::string,
- test_hash<int>,
- test_equal_to<int>,
- other_allocator<std::pair<const int, std::string> > >
- C;
- typedef std::pair<int, std::string> P;
- P a[] = {
- P(1, "one"),
- P(2, "two"),
- P(3, "three"),
- P(4, "four"),
- P(1, "four"),
- P(2, "four"),
- };
- C c0(a,
- a + sizeof(a) / sizeof(a[0]),
- 7,
- test_hash<int>(8),
- test_equal_to<int>(9),
- other_allocator<std::pair<const int, std::string> >(10));
- C c = c0;
- LIBCPP_ASSERT(c.bucket_count() == 7);
- assert(c.size() == 4);
- assert(c.at(1) == "one");
- assert(c.at(2) == "two");
- assert(c.at(3) == "three");
- assert(c.at(4) == "four");
- assert(c.hash_function() == test_hash<int>(8));
- assert(c.key_eq() == test_equal_to<int>(9));
- assert(c.get_allocator() == (other_allocator<std::pair<const int, std::string> >(-2)));
- assert(!c.empty());
- assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
- assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
- assert(std::fabs(c.load_factor() - (float)c.size() / c.bucket_count()) < FLT_EPSILON);
- assert(c.max_load_factor() == 1);
+ { // Copy empty map
+ using Map = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, Alloc>;
+
+ const Map orig;
+ Map copy = orig;
+ LIBCPP_ASSERT(copy.bucket_count() == 0);
+ assert(copy.size() == 0);
+ assert(static_cast<std::size_t>(std::distance(copy.begin(), copy.end())) == copy.size());
+ assert(copy.max_load_factor() == 1.f);
+
+ // Check that orig is still what is expected
+ LIBCPP_ASSERT(orig.bucket_count() == 0);
+ assert(orig.size() == 0);
}
- {
- typedef std::unordered_map<int,
- std::string,
- test_hash<int>,
- test_equal_to<int>,
- min_allocator<std::pair<const int, std::string> > >
- C;
- typedef std::pair<int, std::string> P;
- P a[] = {
- P(1, "one"),
- P(2, "two"),
- P(3, "three"),
- P(4, "four"),
- P(1, "four"),
- P(2, "four"),
- };
- C c0(a,
- a + sizeof(a) / sizeof(a[0]),
- 7,
- test_hash<int>(8),
- test_equal_to<int>(9),
- min_allocator<std::pair<const int, std::string> >());
- C c = c0;
- LIBCPP_ASSERT(c.bucket_count() == 7);
- assert(c.size() == 4);
- assert(c.at(1) == "one");
- assert(c.at(2) == "two");
- assert(c.at(3) == "three");
- assert(c.at(4) == "four");
- assert(c.hash_function() == test_hash<int>(8));
- assert(c.key_eq() == test_equal_to<int>(9));
- assert(c.get_allocator() == (min_allocator<std::pair<const int, std::string> >()));
- assert(!c.empty());
- assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
- assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
- assert(std::fabs(c.load_factor() - (float)c.size() / c.bucket_count()) < FLT_EPSILON);
- assert(c.max_load_factor() == 1);
+ { // Ensure that the hash function is copied
+ using Map = std::unordered_map<int, int, test_hash<int>, std::equal_to<int>, Alloc>;
+ const Map orig(0, test_hash<int>(23));
+ Map copy = orig;
+ assert(copy.hash_function() == test_hash<int>(23));
+
+ // Check that orig is still what is expected
+ assert(orig.hash_function() == test_hash<int>(23));
+ }
+ { // Ensure that the quality comparator is copied
+ using Map = std::unordered_map<int, int, std::hash<int>, test_equal_to<int>, Alloc>;
+ const Map orig(0, std::hash<int>(), test_equal_to<int>(56));
+ Map copy = orig;
+ assert(copy.key_eq() == test_equal_to<int>(56));
+
+ // Check that orig is still what is expected
+ assert(orig.key_eq() == test_equal_to<int>(56));
}
-#endif
+}
+
+void test() {
+ test_alloc<std::allocator<std::pair<const int, int>>>();
+ test_alloc<min_allocator<std::pair<const int, int>>>();
+
+ { // Ensure that the allocator is copied
+ using V = std::pair<const int, int>;
+ using Map = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, test_allocator<V> >;
+
+ V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+ const Map orig(begin(arr), end(arr), 0, test_allocator<V>(10));
+ Map copy = orig;
+ assert(copy.size() == 3);
+ assert(copy.get_allocator() == test_allocator<V>(10));
+
+ // Check that orig is still what is expected
+ assert(orig.size() == 3);
+ assert(orig.get_allocator() == test_allocator<V>(10));
+ assert(orig.get_allocator().get_id() != test_alloc_base::moved_value);
+ }
+
+ { // Ensure that soccc is handled properly
+ using V = std::pair<const int, int>;
+ using Map = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, other_allocator<V> >;
+
+ V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+ const Map orig(begin(arr), end(arr), 0, other_allocator<V>(10));
+ Map copy = orig;
+ assert(copy.size() == 3);
+ assert(copy.get_allocator() == other_allocator<V>(-2));
+
+ // Check that orig is still what is expected
+ assert(orig.size() == 3);
+ assert(orig.get_allocator() == other_allocator<V>(10));
+ }
+}
+
+int main(int, char**) {
+ test();
return 0;
}
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_alloc.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_alloc.pass.cpp
index 65c49f4fdcd00..3660149d1b9fa 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_alloc.pass.cpp
@@ -14,127 +14,89 @@
// unordered_map(const unordered_map& u, const allocator_type& a);
-#include <unordered_map>
-#include <string>
#include <cassert>
#include <cfloat>
#include <cmath>
#include <cstddef>
+#include <unordered_map>
-#include "test_macros.h"
#include "../../../test_compare.h"
#include "../../../test_hash.h"
-#include "test_allocator.h"
#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
-int main(int, char**) {
- {
- typedef std::unordered_map<int,
- std::string,
- test_hash<int>,
- test_equal_to<int>,
- test_allocator<std::pair<const int, std::string> > >
- C;
- typedef std::pair<int, std::string> P;
- P a[] = {
- P(1, "one"),
- P(2, "two"),
- P(3, "three"),
- P(4, "four"),
- P(1, "four"),
- P(2, "four"),
- };
- C c0(a,
- a + sizeof(a) / sizeof(a[0]),
- 7,
- test_hash<int>(8),
- test_equal_to<int>(9),
- test_allocator<std::pair<const int, std::string> >(10));
- C c(c0, test_allocator<std::pair<const int, std::string> >(5));
- LIBCPP_ASSERT(c.bucket_count() == 7);
- assert(c.size() == 4);
- assert(c.at(1) == "one");
- assert(c.at(2) == "two");
- assert(c.at(3) == "three");
- assert(c.at(4) == "four");
- assert(c.hash_function() == test_hash<int>(8));
- assert(c.key_eq() == test_equal_to<int>(9));
- assert(c.get_allocator() == (test_allocator<std::pair<const int, std::string> >(5)));
- assert(!c.empty());
- assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
- assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
- assert(std::fabs(c.load_factor() - (float)c.size() / c.bucket_count()) < FLT_EPSILON);
- assert(c.max_load_factor() == 1);
+template <class Alloc>
+void test_alloc(const Alloc& new_alloc) {
+ { // Simple check
+ using V = std::pair<const int, int>;
+ using Map = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, Alloc>;
+
+ V arr[] = {V(1, 2), V(2, 4), V(3, 1)};
+ const Map orig(std::begin(arr), std::end(arr));
+ Map copy(orig, new_alloc);
+ LIBCPP_ASSERT(copy.bucket_count() == 5);
+ assert(copy.size() == 3);
+ assert(copy.at(1) == 2);
+ assert(copy.at(2) == 4);
+ assert(copy.at(3) == 1);
+ assert(static_cast<std::size_t>(std::distance(copy.begin(), copy.end())) == copy.size());
+ assert(std::fabs(copy.load_factor() - static_cast<float>(copy.size()) / copy.bucket_count()) < FLT_EPSILON);
+ assert(copy.max_load_factor() == 1.f);
+ assert(copy.get_allocator() == new_alloc);
+
+ // Check that orig is still what is expected
+ LIBCPP_ASSERT(orig.bucket_count() == 5);
+ assert(orig.size() == 3);
+ assert(orig.at(1) == 2);
+ assert(orig.at(2) == 4);
+ assert(orig.at(3) == 1);
}
-#if TEST_STD_VER >= 11
- {
- typedef std::unordered_map<int,
- std::string,
- test_hash<int>,
- test_equal_to<int>,
- min_allocator<std::pair<const int, std::string> > >
- C;
- typedef std::pair<int, std::string> P;
- P a[] = {
- P(1, "one"),
- P(2, "two"),
- P(3, "three"),
- P(4, "four"),
- P(1, "four"),
- P(2, "four"),
- };
- C c0(a,
- a + sizeof(a) / sizeof(a[0]),
- 7,
- test_hash<int>(8),
- test_equal_to<int>(9),
- min_allocator<std::pair<const int, std::string> >());
- C c(c0, min_allocator<std::pair<const int, std::string> >());
- LIBCPP_ASSERT(c.bucket_count() == 7);
- assert(c.size() == 4);
- assert(c.at(1) == "one");
- assert(c.at(2) == "two");
- assert(c.at(3) == "three");
- assert(c.at(4) == "four");
- assert(c.hash_function() == test_hash<int>(8));
- assert(c.key_eq() == test_equal_to<int>(9));
- assert(c.get_allocator() == (min_allocator<std::pair<const int, std::string> >()));
- assert(!c.empty());
- assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
- assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
- assert(std::fabs(c.load_factor() - (float)c.size() / c.bucket_count()) < FLT_EPSILON);
- assert(c.max_load_factor() == 1);
+ { // Copy empty map
+ using Map = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, Alloc>;
+
+ const Map orig;
+ Map copy(orig, new_alloc);
+ LIBCPP_ASSERT(copy.bucket_count() == 0);
+ assert(copy.size() == 0);
+ assert(static_cast<std::size_t>(std::distance(copy.begin(), copy.end())) == copy.size());
+ assert(copy.max_load_factor() == 1.f);
+ assert(copy.get_allocator() == new_alloc);
+
+ // Check that orig is still what is expected
+ LIBCPP_ASSERT(orig.bucket_count() == 0);
+ assert(orig.size() == 0);
}
- {
- typedef explicit_allocator<std::pair<const int, std::string>> A;
- typedef std::unordered_map<int, std::string, test_hash<int>, test_equal_to<int>, A > C;
- typedef std::pair<int, std::string> P;
- P a[] = {
- P(1, "one"),
- P(2, "two"),
- P(3, "three"),
- P(4, "four"),
- P(1, "four"),
- P(2, "four"),
- };
- C c0(a, a + sizeof(a) / sizeof(a[0]), 7, test_hash<int>(8), test_equal_to<int>(9), A{});
- C c(c0, A{});
- LIBCPP_ASSERT(c.bucket_count() == 7);
- assert(c.size() == 4);
- assert(c.at(1) == "one");
- assert(c.at(2) == "two");
- assert(c.at(3) == "three");
- assert(c.at(4) == "four");
- assert(c.hash_function() == test_hash<int>(8));
- assert(c.key_eq() == test_equal_to<int>(9));
- assert(c.get_allocator() == A{});
- assert(!c.empty());
- assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
- assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
- assert(std::fabs(c.load_factor() - (float)c.size() / c.bucket_count()) < FLT_EPSILON);
- assert(c.max_load_factor() == 1);
+ { // Ensure that the hash function is copied
+ using Map = std::unordered_map<int, int, test_hash<int>, std::equal_to<int>, Alloc>;
+ const Map orig(0, test_hash<int>(23));
+ Map copy(orig, new_alloc);
+ assert(copy.hash_function() == test_hash<int>(23));
+ assert(copy.get_allocator() == new_alloc);
+
+ // Check that orig is still what is expected
+ assert(orig.hash_function() == test_hash<int>(23));
}
-#endif
+ { // Ensure that the quality comparator is copied
+ using Map = std::unordered_map<int, int, std::hash<int>, test_equal_to<int>, Alloc>;
+ const Map orig(0, std::hash<int>(), test_equal_to<int>(56));
+ Map copy(orig, new_alloc);
+ assert(copy.key_eq() == test_equal_to<int>(56));
+ assert(copy.get_allocator() == new_alloc);
+
+ // Check that orig is still what is expected
+ assert(orig.key_eq() == test_equal_to<int>(56));
+ }
+}
+
+void test() {
+ test_alloc(std::allocator<std::pair<const int, int>>());
+ test_alloc(min_allocator<std::pair<const int, int>>());
+ test_alloc(test_allocator<std::pair<const int, int>>(25));
+}
+
+int main(int, char**) {
+ test();
return 0;
}
More information about the libcxx-commits
mailing list