diff options
| Commit | c68bc3a0439f8ba944d789b8407fe620dfdc107f (patch) | |
|---|---|---|
| Author | 2020-12-24 11:00:59 +0200 | |
| Committer | 2020-12-24 11:00:59 +0200 | |
| Files | 7eb46ce107c4d1435be2460ae9ac504553ce4324 /unordered_map.hpp | |
| IPL-c68bc3a0439f8ba944d789b8407fe620dfdc107f.tar IPL-c68bc3a0439f8ba944d789b8407fe620dfdc107f.tar.gz IPL-c68bc3a0439f8ba944d789b8407fe620dfdc107f.zip | ||
Squashed 'gctest/flat_hash_map/' content from commit 2c46874
git-subtree-dir: gctest/flat_hash_map
git-subtree-split: 2c4687431f978f02a3780e24b8b701d22aa32d9c
1 files changed, 885 insertions, 0 deletions
| -rw-r--r-- | unordered_map.hpp | 885 |
diff --git a/unordered_map.hpp b/unordered_map.hpp new file mode 100644 index 0000000..5e6e876 --- /dev/null +++ b/unordered_map.hpp @@ -0,0 +1,885 @@ +// Copyright Malte Skarupke 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include <cstdint> +#include <cstddef> +#include <cmath> +#include <array> +#include <algorithm> +#include <iterator> +#include <utility> +#include <type_traits> +#include "flat_hash_map.hpp" + +namespace ska +{ + +namespace detailv10 +{ +template<typename T, typename Allocator> +struct sherwood_v10_entry +{ + sherwood_v10_entry() + { + } + ~sherwood_v10_entry() + { + } + + using EntryPointer = typename std::allocator_traits<typename std::allocator_traits<Allocator>::template rebind_alloc<sherwood_v10_entry>>::pointer; + + EntryPointer next = nullptr; + union + { + T value; + }; + + static EntryPointer * empty_pointer() + { + static EntryPointer result[3] = { EntryPointer(nullptr) + ptrdiff_t(1), nullptr, nullptr }; + return result + 1; + } +}; + +using ska::detailv3::functor_storage; +using ska::detailv3::KeyOrValueHasher; +using ska::detailv3::KeyOrValueEquality; +using ska::detailv3::AssignIfTrue; +using ska::detailv3::HashPolicySelector; + +template<typename T, typename FindKey, typename ArgumentHash, typename Hasher, typename ArgumentEqual, typename Equal, typename ArgumentAlloc, typename EntryAlloc, typename BucketAllocator> +class sherwood_v10_table : private EntryAlloc, private Hasher, private Equal, private BucketAllocator +{ + using Entry = detailv10::sherwood_v10_entry<T, ArgumentAlloc>; + using AllocatorTraits = std::allocator_traits<EntryAlloc>; + using BucketAllocatorTraits = std::allocator_traits<BucketAllocator>; + using EntryPointer = typename AllocatorTraits::pointer; + struct convertible_to_iterator; + +public: + + using value_type = T; + using size_type = size_t; + using difference_type = std::ptrdiff_t; + using hasher = ArgumentHash; + using key_equal = ArgumentEqual; + using allocator_type = EntryAlloc; + using reference = value_type &; + using const_reference = const value_type &; + using pointer = value_type *; + using const_pointer = const value_type *; + + sherwood_v10_table() + { + } + explicit sherwood_v10_table(size_type bucket_count, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) + : EntryAlloc(alloc), Hasher(hash), Equal(equal), BucketAllocator(alloc) + { + rehash(bucket_count); + } + sherwood_v10_table(size_type bucket_count, const ArgumentAlloc & alloc) + : sherwood_v10_table(bucket_count, ArgumentHash(), ArgumentEqual(), alloc) + { + } + sherwood_v10_table(size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) + : sherwood_v10_table(bucket_count, hash, ArgumentEqual(), alloc) + { + } + explicit sherwood_v10_table(const ArgumentAlloc & alloc) + : EntryAlloc(alloc), BucketAllocator(alloc) + { + } + template<typename It> + sherwood_v10_table(It first, It last, size_type bucket_count = 0, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) + : sherwood_v10_table(bucket_count, hash, equal, alloc) + { + insert(first, last); + } + template<typename It> + sherwood_v10_table(It first, It last, size_type bucket_count, const ArgumentAlloc & alloc) + : sherwood_v10_table(first, last, bucket_count, ArgumentHash(), ArgumentEqual(), alloc) + { + } + template<typename It> + sherwood_v10_table(It first, It last, size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) + : sherwood_v10_table(first, last, bucket_count, hash, ArgumentEqual(), alloc) + { + } + sherwood_v10_table(std::initializer_list<T> il, size_type bucket_count = 0, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) + : sherwood_v10_table(bucket_count, hash, equal, alloc) + { + if (bucket_count == 0) + reserve(il.size()); + insert(il.begin(), il.end()); + } + sherwood_v10_table(std::initializer_list<T> il, size_type bucket_count, const ArgumentAlloc & alloc) + : sherwood_v10_table(il, bucket_count, ArgumentHash(), ArgumentEqual(), alloc) + { + } + sherwood_v10_table(std::initializer_list<T> il, size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) + : sherwood_v10_table(il, bucket_count, hash, ArgumentEqual(), alloc) + { + } + sherwood_v10_table(const sherwood_v10_table & other) + : sherwood_v10_table(other, AllocatorTraits::select_on_container_copy_construction(other.get_allocator())) + { + } + sherwood_v10_table(const sherwood_v10_table & other, const ArgumentAlloc & alloc) + : EntryAlloc(alloc), Hasher(other), Equal(other), BucketAllocator(alloc), _max_load_factor(other._max_load_factor) + { + try + { + rehash_for_other_container(other); + insert(other.begin(), other.end()); + } + catch(...) + { + clear(); + deallocate_data(); + throw; + } + } + sherwood_v10_table(sherwood_v10_table && other) noexcept + : EntryAlloc(std::move(other)), Hasher(std::move(other)), Equal(std::move(other)), BucketAllocator(std::move(other)), _max_load_factor(other._max_load_factor) + { + swap_pointers(other); + } + sherwood_v10_table(sherwood_v10_table && other, const ArgumentAlloc & alloc) noexcept + : EntryAlloc(alloc), Hasher(std::move(other)), Equal(std::move(other)), BucketAllocator(alloc), _max_load_factor(other._max_load_factor) + { + swap_pointers(other); + } + sherwood_v10_table & operator=(const sherwood_v10_table & other) + { + if (this == std::addressof(other)) + return *this; + + clear(); + static_assert(AllocatorTraits::propagate_on_container_copy_assignment::value == BucketAllocatorTraits::propagate_on_container_copy_assignment::value, "The allocators have to behave the same way"); + if (AllocatorTraits::propagate_on_container_copy_assignment::value) + { + if (static_cast<EntryAlloc &>(*this) != static_cast<const EntryAlloc &>(other) || static_cast<BucketAllocator &>(*this) != static_cast<const BucketAllocator &>(other)) + { + reset_to_empty_state(); + } + AssignIfTrue<EntryAlloc, AllocatorTraits::propagate_on_container_copy_assignment::value>()(*this, other); + AssignIfTrue<BucketAllocator, BucketAllocatorTraits::propagate_on_container_copy_assignment::value>()(*this, other); + } + _max_load_factor = other._max_load_factor; + static_cast<Hasher &>(*this) = other; + static_cast<Equal &>(*this) = other; + rehash_for_other_container(other); + insert(other.begin(), other.end()); + return *this; + } + sherwood_v10_table & operator=(sherwood_v10_table && other) noexcept + { + static_assert(AllocatorTraits::propagate_on_container_move_assignment::value == BucketAllocatorTraits::propagate_on_container_move_assignment::value, "The allocators have to behave the same way"); + if (this == std::addressof(other)) + return *this; + else if (AllocatorTraits::propagate_on_container_move_assignment::value) + { + clear(); + reset_to_empty_state(); + AssignIfTrue<EntryAlloc, AllocatorTraits::propagate_on_container_move_assignment::value>()(*this, std::move(other)); + AssignIfTrue<BucketAllocator, BucketAllocatorTraits::propagate_on_container_move_assignment::value>()(*this, std::move(other)); + swap_pointers(other); + } + else if (static_cast<EntryAlloc &>(*this) == static_cast<EntryAlloc &>(other) && static_cast<BucketAllocator &>(*this) == static_cast<BucketAllocator &>(other)) + { + swap_pointers(other); + } + else + { + clear(); + _max_load_factor = other._max_load_factor; + rehash_for_other_container(other); + for (T & elem : other) + emplace(std::move(elem)); + other.clear(); + } + static_cast<Hasher &>(*this) = std::move(other); + static_cast<Equal &>(*this) = std::move(other); + return *this; + } + ~sherwood_v10_table() + { + clear(); + deallocate_data(); + } + + const allocator_type & get_allocator() const + { + return static_cast<const allocator_type &>(*this); + } + const ArgumentEqual & key_eq() const + { + return static_cast<const ArgumentEqual &>(*this); + } + const ArgumentHash & hash_function() const + { + return static_cast<const ArgumentHash &>(*this); + } + + template<typename ValueType> + struct templated_iterator + { + templated_iterator() + { + } + templated_iterator(EntryPointer element, EntryPointer * bucket) + : current_element(element), current_bucket(bucket) + { + } + + EntryPointer current_element = nullptr; + EntryPointer * current_bucket = nullptr; + + using iterator_category = std::forward_iterator_tag; + using value_type = ValueType; + using difference_type = ptrdiff_t; + using pointer = ValueType *; + using reference = ValueType &; + + friend bool operator==(const templated_iterator & lhs, const templated_iterator & rhs) + { + return lhs.current_element == rhs.current_element; + } + friend bool operator!=(const templated_iterator & lhs, const templated_iterator & rhs) + { + return !(lhs == rhs); + } + + templated_iterator & operator++() + { + if (!current_element->next) + { + do + { + --current_bucket; + } + while (!*current_bucket); + current_element = *current_bucket; + } + else + current_element = current_element->next; + return *this; + } + templated_iterator operator++(int) + { + templated_iterator copy(*this); + ++*this; + return copy; + } + + ValueType & operator*() const + { + return current_element->value; + } + ValueType * operator->() const + { + return std::addressof(current_element->value); + } + + operator templated_iterator<const value_type>() const + { + return { current_element, current_bucket }; + } + }; + using iterator = templated_iterator<value_type>; + using const_iterator = templated_iterator<const value_type>; + + iterator begin() + { + EntryPointer * end = entries - 1; + for (EntryPointer * it = entries + num_slots_minus_one; it != end; --it) + { + if (*it) + return { *it, it }; + } + return { *end, end }; + } + const_iterator begin() const + { + return const_cast<sherwood_v10_table *>(this)->begin(); + } + const_iterator cbegin() const + { + return begin(); + } + iterator end() + { + EntryPointer * end = entries - 1; + return { *end, end }; + } + const_iterator end() const + { + EntryPointer * end = entries - 1; + return { *end, end }; + } + const_iterator cend() const + { + return end(); + } + + iterator find(const FindKey & key) + { + size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); + EntryPointer * bucket = entries + ptrdiff_t(index); + for (EntryPointer it = *bucket; it; it = it->next) + { + if (compares_equal(key, it->value)) + return { it, bucket }; + } + return end(); + } + const_iterator find(const FindKey & key) const + { + return const_cast<sherwood_v10_table *>(this)->find(key); + } + size_t count(const FindKey & key) const + { + return find(key) == end() ? 0 : 1; + } + std::pair<iterator, iterator> equal_range(const FindKey & key) + { + iterator found = find(key); + if (found == end()) + return { found, found }; + else + return { found, std::next(found) }; + } + std::pair<const_iterator, const_iterator> equal_range(const FindKey & key) const + { + const_iterator found = find(key); + if (found == end()) + return { found, found }; + else + return { found, std::next(found) }; + } + + template<typename Key, typename... Args> + std::pair<iterator, bool> emplace(Key && key, Args &&... args) + { + size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); + EntryPointer * bucket = entries + ptrdiff_t(index); + for (EntryPointer it = *bucket; it; it = it->next) + { + if (compares_equal(key, it->value)) + return { { it, bucket }, false }; + } + return emplace_new_key(bucket, std::forward<Key>(key), std::forward<Args>(args)...); + } + + std::pair<iterator, bool> insert(const value_type & value) + { + return emplace(value); + } + std::pair<iterator, bool> insert(value_type && value) + { + return emplace(std::move(value)); + } + template<typename... Args> + iterator emplace_hint(const_iterator, Args &&... args) + { + return emplace(std::forward<Args>(args)...).first; + } + iterator insert(const_iterator, const value_type & value) + { + return emplace(value).first; + } + iterator insert(const_iterator, value_type && value) + { + return emplace(std::move(value)).first; + } + + template<typename It> + void insert(It begin, It end) + { + for (; begin != end; ++begin) + { + emplace(*begin); + } + } + void insert(std::initializer_list<value_type> il) + { + insert(il.begin(), il.end()); + } + + void rehash(size_t num_buckets) + { + num_buckets = std::max(num_buckets, static_cast<size_t>(std::ceil(num_elements / static_cast<double>(_max_load_factor)))); + if (num_buckets == 0) + { + reset_to_empty_state(); + return; + } + auto new_prime_index = hash_policy.next_size_over(num_buckets); + if (num_buckets == bucket_count()) + return; + EntryPointer * new_buckets(&*BucketAllocatorTraits::allocate(*this, num_buckets + 1)); + EntryPointer * end_it = new_buckets + static_cast<ptrdiff_t>(num_buckets + 1); + *new_buckets = EntryPointer(nullptr) + ptrdiff_t(1); + ++new_buckets; + std::fill(new_buckets, end_it, nullptr); + std::swap(entries, new_buckets); + std::swap(num_slots_minus_one, num_buckets); + --num_slots_minus_one; + hash_policy.commit(new_prime_index); + if (!num_buckets) + return; + + for (EntryPointer * it = new_buckets, * end = it + static_cast<ptrdiff_t>(num_buckets + 1); it != end; ++it) + { + for (EntryPointer e = *it; e;) + { + EntryPointer next = e->next; + size_t index = hash_policy.index_for_hash(hash_object(e->value), num_slots_minus_one); + EntryPointer & new_slot = entries[index]; + e->next = new_slot; + new_slot = e; + e = next; + } + } + BucketAllocatorTraits::deallocate(*this, new_buckets - 1, num_buckets + 2); + } + + void reserve(size_t num_elements) + { + if (!num_elements) + return; + num_elements = static_cast<size_t>(std::ceil(num_elements / static_cast<double>(_max_load_factor))); + if (num_elements > bucket_count()) + rehash(num_elements); + } + + // the return value is a type that can be converted to an iterator + // the reason for doing this is that it's not free to find the + // iterator pointing at the next element. if you care about the + // next iterator, turn the return value into an iterator + convertible_to_iterator erase(const_iterator to_erase) + { + --num_elements; + AllocatorTraits::destroy(*this, std::addressof(to_erase.current_element->value)); + EntryPointer * previous = to_erase.current_bucket; + while (*previous != to_erase.current_element) + { + previous = &(*previous)->next; + } + *previous = to_erase.current_element->next; + AllocatorTraits::deallocate(*this, to_erase.current_element, 1); + return { *previous, to_erase.current_bucket }; + } + + convertible_to_iterator erase(const_iterator begin_it, const_iterator end_it) + { + while (begin_it.current_bucket != end_it.current_bucket) + { + begin_it = erase(begin_it); + } + EntryPointer * bucket = begin_it.current_bucket; + EntryPointer * previous = bucket; + while (*previous != begin_it.current_element) + previous = &(*previous)->next; + while (*previous != end_it.current_element) + { + --num_elements; + EntryPointer entry = *previous; + AllocatorTraits::destroy(*this, std::addressof(entry->value)); + *previous = entry->next; + AllocatorTraits::deallocate(*this, entry, 1); + } + return { *previous, bucket }; + } + + size_t erase(const FindKey & key) + { + auto found = find(key); + if (found == end()) + return 0; + else + { + erase(found); + return 1; + } + } + + void clear() + { + if (!num_slots_minus_one) + return; + for (EntryPointer * it = entries, * end = it + static_cast<ptrdiff_t>(num_slots_minus_one + 1); it != end; ++it) + { + for (EntryPointer e = *it; e;) + { + EntryPointer next = e->next; + AllocatorTraits::destroy(*this, std::addressof(e->value)); + AllocatorTraits::deallocate(*this, e, 1); + e = next; + } + *it = nullptr; + } + num_elements = 0; + } + + void swap(sherwood_v10_table & other) + { + using std::swap; + swap_pointers(other); + swap(static_cast<ArgumentHash &>(*this), static_cast<ArgumentHash &>(other)); + swap(static_cast<ArgumentEqual &>(*this), static_cast<ArgumentEqual &>(other)); + if (AllocatorTraits::propagate_on_container_swap::value) + swap(static_cast<EntryAlloc &>(*this), static_cast<EntryAlloc &>(other)); + if (BucketAllocatorTraits::propagate_on_container_swap::value) + swap(static_cast<BucketAllocator &>(*this), static_cast<BucketAllocator &>(other)); + } + + size_t size() const + { + return num_elements; + } + size_t max_size() const + { + return (AllocatorTraits::max_size(*this)) / sizeof(Entry); + } + size_t bucket_count() const + { + return num_slots_minus_one + 1; + } + size_type max_bucket_count() const + { + return (AllocatorTraits::max_size(*this) - 1) / sizeof(Entry); + } + size_t bucket(const FindKey & key) const + { + return hash_policy.template index_for_hash<0>(hash_object(key), num_slots_minus_one); + } + float load_factor() const + { + size_t buckets = bucket_count(); + if (buckets) + return static_cast<float>(num_elements) / bucket_count(); + else + return 0; + } + void max_load_factor(float value) + { + _max_load_factor = value; + } + float max_load_factor() const + { + return _max_load_factor; + } + + bool empty() const + { + return num_elements == 0; + } + +private: + EntryPointer * entries = Entry::empty_pointer(); + size_t num_slots_minus_one = 0; + typename HashPolicySelector<ArgumentHash>::type hash_policy; + float _max_load_factor = 1.0f; + size_t num_elements = 0; + + void rehash_for_other_container(const sherwood_v10_table & other) + { + reserve(other.size()); + } + + void swap_pointers(sherwood_v10_table & other) + { + using std::swap; + swap(hash_policy, other.hash_policy); + swap(entries, other.entries); + swap(num_slots_minus_one, other.num_slots_minus_one); + swap(num_elements, other.num_elements); + swap(_max_load_factor, other._max_load_factor); + } + + template<typename... Args> + SKA_NOINLINE(std::pair<iterator, bool>) emplace_new_key(EntryPointer * bucket, Args &&... args) + { + using std::swap; + if (is_full()) + { + grow(); + return emplace(std::forward<Args>(args)...); + } + else + { + EntryPointer new_entry = AllocatorTraits::allocate(*this, 1); + try + { + AllocatorTraits::construct(*this, std::addressof(new_entry->value), std::forward<Args>(args)...); + } + catch(...) + { + AllocatorTraits::deallocate(*this, new_entry, 1); + throw; + } + ++num_elements; + new_entry->next = *bucket; + *bucket = new_entry; + return { { new_entry, bucket }, true }; + } + } + + bool is_full() const + { + if (!num_slots_minus_one) + return true; + else + return num_elements + 1 > (num_slots_minus_one + 1) * static_cast<double>(_max_load_factor); + } + + void grow() + { + rehash(std::max(size_t(4), 2 * bucket_count())); + } + + void deallocate_data() + { + if (entries != Entry::empty_pointer()) + { + BucketAllocatorTraits::deallocate(*this, entries - 1, num_slots_minus_one + 2); + } + } + + void reset_to_empty_state() + { + deallocate_data(); + entries = Entry::empty_pointer(); + num_slots_minus_one = 0; + hash_policy.reset(); + } + + template<typename U> + size_t hash_object(const U & key) + { + return static_cast<Hasher &>(*this)(key); + } + template<typename U> + size_t hash_object(const U & key) const + { + return static_cast<const Hasher &>(*this)(key); + } + template<typename L, typename R> + bool compares_equal(const L & lhs, const R & rhs) + { + return static_cast<Equal &>(*this)(lhs, rhs); + } + + struct convertible_to_iterator + { + EntryPointer element; + EntryPointer * bucket; + + operator iterator() + { + if (element) + return { element, bucket }; + else + { + do + { + --bucket; + } + while (!*bucket); + return { *bucket, bucket }; + } + } + operator const_iterator() + { + if (element) + return { element, bucket }; + else + { + do + { + --bucket; + } + while (!*bucket); + return { *bucket, bucket }; + } + } + }; +}; +} + + +template<typename K, typename V, typename H = std::hash<K>, typename E = std::equal_to<K>, typename A = std::allocator<std::pair<K, V> > > +class unordered_map + : public detailv10::sherwood_v10_table + < + std::pair<K, V>, + K, + H, + detailv10::KeyOrValueHasher<K, std::pair<K, V>, H>, + E, + detailv10::KeyOrValueEquality<K, std::pair<K, V>, E>, + A, + typename std::allocator_traits<A>::template rebind_alloc<detailv10::sherwood_v10_entry<std::pair<K, V>, A>>, + typename std::allocator_traits<A>::template rebind_alloc<typename std::allocator_traits<A>::template rebind_traits<detailv10::sherwood_v10_entry<std::pair<K, V>, A>>::pointer> + > +{ + using Table = detailv10::sherwood_v10_table + < + std::pair<K, V>, + K, + H, + detailv10::KeyOrValueHasher<K, std::pair<K, V>, H>, + E, + detailv10::KeyOrValueEquality<K, std::pair<K, V>, E>, + A, + typename std::allocator_traits<A>::template rebind_alloc<detailv10::sherwood_v10_entry<std::pair<K, V>, A>>, + typename std::allocator_traits<A>::template rebind_alloc<typename std::allocator_traits<A>::template rebind_traits<detailv10::sherwood_v10_entry<std::pair<K, V>, A>>::pointer> + >; +public: + + using key_type = K; + using mapped_type = V; + + using Table::Table; + unordered_map() + { + } + + V & operator[](const K & key) + { + return emplace(key, convertible_to_value()).first->second; + } + V & operator[](K && key) + { + return emplace(std::move(key), convertible_to_value()).first->second; + } + V & at(const K & key) + { + auto found = this->find(key); + if (found == this->end()) + throw std::out_of_range("Argument passed to at() was not in the map."); + return found->second; + } + const V & at(const K & key) const + { + auto found = this->find(key); + if (found == this->end()) + throw std::out_of_range("Argument passed to at() was not in the map."); + return found->second; + } + + using Table::emplace; + std::pair<typename Table::iterator, bool> emplace() + { + return emplace(key_type(), convertible_to_value()); + } + + friend bool operator==(const unordered_map & lhs, const unordered_map & rhs) + { + if (lhs.size() != rhs.size()) + return false; + for (const typename Table::value_type & value : lhs) + { + auto found = rhs.find(value.first); + if (found == rhs.end()) + return false; + else if (value.second != found->second) + return false; + } + return true; + } + friend bool operator!=(const unordered_map & lhs, const unordered_map & rhs) + { + return !(lhs == rhs); + } + +private: + struct convertible_to_value + { + operator V() const + { + return V(); + } + }; +}; + +template<typename T, typename H = std::hash<T>, typename E = std::equal_to<T>, typename A = std::allocator<T> > +class unordered_set + : public detailv10::sherwood_v10_table + < + T, + T, + H, + detailv10::functor_storage<size_t, H>, + E, + detailv10::functor_storage<bool, E>, + A, + typename std::allocator_traits<A>::template rebind_alloc<detailv10::sherwood_v10_entry<T, A>>, + typename std::allocator_traits<A>::template rebind_alloc<typename std::allocator_traits<A>::template rebind_traits<detailv10::sherwood_v10_entry<T, A>>::pointer> + > +{ + using Table = detailv10::sherwood_v10_table + < + T, + T, + H, + detailv10::functor_storage<size_t, H>, + E, + detailv10::functor_storage<bool, E>, + A, + typename std::allocator_traits<A>::template rebind_alloc<detailv10::sherwood_v10_entry<T, A>>, + typename std::allocator_traits<A>::template rebind_alloc<typename std::allocator_traits<A>::template rebind_traits<detailv10::sherwood_v10_entry<T, A>>::pointer> + >; +public: + + using key_type = T; + + using Table::Table; + unordered_set() + { + } + + template<typename... Args> + std::pair<typename Table::iterator, bool> emplace(Args &&... args) + { + return Table::emplace(T(std::forward<Args>(args)...)); + } + std::pair<typename Table::iterator, bool> emplace(const key_type & arg) + { + return Table::emplace(arg); + } + std::pair<typename Table::iterator, bool> emplace(key_type & arg) + { + return Table::emplace(arg); + } + std::pair<typename Table::iterator, bool> emplace(const key_type && arg) + { + return Table::emplace(std::move(arg)); + } + std::pair<typename Table::iterator, bool> emplace(key_type && arg) + { + return Table::emplace(std::move(arg)); + } + + friend bool operator==(const unordered_set & lhs, const unordered_set & rhs) + { + if (lhs.size() != rhs.size()) + return false; + for (const T & value : lhs) + { + if (rhs.find(value) == rhs.end()) + return false; + } + return true; + } + friend bool operator!=(const unordered_set & lhs, const unordered_set & rhs) + { + return !(lhs == rhs); + } +}; + +} // end namespace ska |
