EnTT 4.0.0
Loading...
Searching...
No Matches
dense_set.hpp
1#ifndef ENTT_CONTAINER_DENSE_SET_HPP
2#define ENTT_CONTAINER_DENSE_SET_HPP
3
4#include <compare>
5#include "../config/config.h"
6#include "../core/bit.hpp"
7#include "../core/compressed_pair.hpp"
8#include "../core/type_traits.hpp"
9#include "../stl/bit.hpp"
10#include "../stl/cmath.hpp"
11#include "../stl/concepts.hpp"
12#include "../stl/cstddef.hpp"
13#include "../stl/functional.hpp"
14#include "../stl/iterator.hpp"
15#include "../stl/limits.hpp"
16#include "../stl/memory.hpp"
17#include "../stl/tuple.hpp"
18#include "../stl/type_traits.hpp"
19#include "../stl/utility.hpp"
20#include "../stl/vector.hpp"
21#include "fwd.hpp"
22
23namespace entt {
24
26namespace internal {
27
28static constexpr stl::size_t dense_set_placeholder_position = (stl::numeric_limits<stl::size_t>::max)();
29
30template<typename It>
31class dense_set_iterator final {
32 template<typename>
33 friend class dense_set_iterator;
34
35 static_assert(stl::is_pointer_v<It>, "Not a pointer type");
36
37public:
38 using value_type = stl::remove_const_t<stl::remove_pointer_t<It>>::second_type;
39 using pointer = const value_type *;
40 using reference = const value_type &;
41 using difference_type = stl::ptrdiff_t;
42 using iterator_category = stl::random_access_iterator_tag;
43
44 constexpr dense_set_iterator() noexcept
45 : it{} {}
46
47 constexpr dense_set_iterator(const It iter) noexcept
48 : it{iter} {}
49
50 template<typename Other>
51 requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
52 constexpr dense_set_iterator(const dense_set_iterator<Other> &other) noexcept
53 : it{other.it} {}
54
55 constexpr dense_set_iterator &operator++() noexcept {
56 return ++it, *this;
57 }
58
59 constexpr dense_set_iterator operator++(int) noexcept {
60 const dense_set_iterator orig = *this;
61 return ++(*this), orig;
62 }
63
64 constexpr dense_set_iterator &operator--() noexcept {
65 return --it, *this;
66 }
67
68 constexpr dense_set_iterator operator--(int) noexcept {
69 const dense_set_iterator orig = *this;
70 return operator--(), orig;
71 }
72
73 constexpr dense_set_iterator &operator+=(const difference_type value) noexcept {
74 it += value;
75 return *this;
76 }
77
78 constexpr dense_set_iterator operator+(const difference_type value) const noexcept {
79 dense_set_iterator copy = *this;
80 return (copy += value);
81 }
82
83 constexpr dense_set_iterator &operator-=(const difference_type value) noexcept {
84 return (*this += -value);
85 }
86
87 constexpr dense_set_iterator operator-(const difference_type value) const noexcept {
88 return (*this + -value);
89 }
90
91 [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
92 return it[value].second;
93 }
94
95 [[nodiscard]] constexpr pointer operator->() const noexcept {
96 return stl::addressof(operator[](0));
97 }
98
99 [[nodiscard]] constexpr reference operator*() const noexcept {
100 return operator[](0);
101 }
102
103 template<typename Other>
104 [[nodiscard]] constexpr stl::ptrdiff_t operator-(const dense_set_iterator<Other> &other) const noexcept {
105 return it - other.it;
106 }
107
108 template<typename Other>
109 [[nodiscard]] constexpr bool operator==(const dense_set_iterator<Other> &other) const noexcept {
110 return it == other.it;
111 }
112
113 template<typename Other>
114 [[nodiscard]] constexpr auto operator<=>(const dense_set_iterator<Other> &other) const noexcept {
115 return it <=> other.it;
116 }
117
118private:
119 It it;
120};
121
122template<typename It>
123class dense_set_local_iterator final {
124 template<typename>
125 friend class dense_set_local_iterator;
126
127 static_assert(stl::is_pointer_v<It>, "Not a pointer type");
128
129public:
130 using value_type = stl::remove_const_t<stl::remove_pointer_t<It>>::second_type;
131 using pointer = const value_type *;
132 using reference = const value_type &;
133 using difference_type = stl::ptrdiff_t;
134 using iterator_category = stl::forward_iterator_tag;
135
136 constexpr dense_set_local_iterator() noexcept = default;
137
138 constexpr dense_set_local_iterator(It iter, const stl::size_t pos) noexcept
139 : it{iter},
140 offset{pos} {}
141
142 template<typename Other>
143 requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
144 constexpr dense_set_local_iterator(const dense_set_local_iterator<Other> &other) noexcept
145 : it{other.it},
146 offset{other.offset} {}
147
148 constexpr dense_set_local_iterator &operator++() noexcept {
149 return offset = it[static_cast<difference_type>(offset)].first, *this;
150 }
151
152 constexpr dense_set_local_iterator operator++(int) noexcept {
153 const dense_set_local_iterator orig = *this;
154 return ++(*this), orig;
155 }
156
157 [[nodiscard]] constexpr pointer operator->() const noexcept {
158 return stl::addressof(it[static_cast<difference_type>(offset)].second);
159 }
160
161 [[nodiscard]] constexpr reference operator*() const noexcept {
162 return *operator->();
163 }
164
165 template<typename Other>
166 [[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Other> &other) const noexcept {
167 return offset == other.offset;
168 }
169
170 [[nodiscard]] constexpr stl::size_t index() const noexcept {
171 return offset;
172 }
173
174private:
175 It it{};
176 stl::size_t offset{dense_set_placeholder_position};
177};
178
179} // namespace internal
181
194template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
196 static constexpr float default_threshold = 0.875f;
197 static constexpr stl::size_t minimum_capacity = 8u;
198 static constexpr stl::size_t placeholder_position = internal::dense_set_placeholder_position;
199
200 using node_type = stl::pair<stl::size_t, Type>;
201 using alloc_traits = stl::allocator_traits<Allocator>;
202 static_assert(stl::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
203 using sparse_container_type = stl::vector<stl::size_t, typename alloc_traits::template rebind_alloc<stl::size_t>>;
204 using packed_container_type = stl::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
205
206 [[nodiscard]] stl::size_t value_to_bucket(const auto &value) const noexcept {
207 return fast_mod(static_cast<size_type>(sparse.second()(value)), bucket_count());
208 }
209
210 [[nodiscard]] auto constrained_find(const auto &value, const stl::size_t bucket) {
211 for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
212 if(packed.second()(packed.first()[offset].second, value)) {
213 return begin() + static_cast<iterator::difference_type>(offset);
214 }
215 }
216
217 return end();
218 }
219
220 [[nodiscard]] auto constrained_find(const auto &value, const stl::size_t bucket) const {
221 for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
222 if(packed.second()(packed.first()[offset].second, value)) {
223 return cbegin() + static_cast<const_iterator::difference_type>(offset);
224 }
225 }
226
227 return cend();
228 }
229
230 template<typename Other>
231 [[nodiscard]] auto insert_or_do_nothing(Other &&value) {
232 const auto index = value_to_bucket(value);
233
234 if(auto it = constrained_find(value, index); it != end()) {
235 return stl::make_pair(it, false);
236 }
237
238 packed.first().emplace_back(sparse.first()[index], stl::forward<Other>(value));
239 sparse.first()[index] = packed.first().size() - 1u;
240 rehash_if_required();
241
242 return stl::make_pair(--end(), true);
243 }
244
245 void move_and_pop(const stl::size_t pos) {
246 if(const auto last = size() - 1u; pos != last) {
247 size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)];
248 packed.first()[pos] = stl::move(packed.first().back());
249 for(; *curr != last; curr = &packed.first()[*curr].first) {}
250 *curr = pos;
251 }
252
253 packed.first().pop_back();
254 }
255
256 void rehash_if_required() {
257 if(const auto bc = bucket_count(); size() > static_cast<size_type>(static_cast<float>(bc) * max_load_factor())) {
258 rehash(bc * 2u);
259 }
260 }
261
262public:
264 using allocator_type = Allocator;
266 using key_type = Type;
268 using value_type = Type;
270 using size_type = stl::size_t;
272 using difference_type = stl::ptrdiff_t;
274 using hasher = Hash;
276 using key_equal = KeyEqual;
278 using iterator = internal::dense_set_iterator<typename packed_container_type::pointer>;
280 using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_pointer>;
282 using reverse_iterator = stl::reverse_iterator<iterator>;
284 using const_reverse_iterator = stl::reverse_iterator<const_iterator>;
286 using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::pointer>;
288 using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_pointer>;
289
292 : dense_set{minimum_capacity} {}
293
298 explicit dense_set(const allocator_type &allocator)
299 : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
300
307 dense_set(const size_type cnt, const allocator_type &allocator)
308 : dense_set{cnt, hasher{}, key_equal{}, allocator} {}
309
317 dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator)
318 : dense_set{cnt, hash, key_equal{}, allocator} {}
319
328 explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
329 : sparse{allocator, hash},
330 packed{allocator, equal} {
331 rehash(cnt);
332 }
333
335 dense_set(const dense_set &) = default;
336
342 dense_set(const dense_set &other, const allocator_type &allocator)
343 : sparse{stl::piecewise_construct, stl::forward_as_tuple(other.sparse.first(), allocator), stl::forward_as_tuple(other.sparse.second())},
344 packed{stl::piecewise_construct, stl::forward_as_tuple(other.packed.first(), allocator), stl::forward_as_tuple(other.packed.second())},
345 threshold{other.threshold} {}
346
348 dense_set(dense_set &&) noexcept = default;
349
355 dense_set(dense_set &&other, const allocator_type &allocator)
356 : sparse{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.sparse.first()), allocator), stl::forward_as_tuple(stl::move(other.sparse.second()))},
357 packed{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.packed.first()), allocator), stl::forward_as_tuple(stl::move(other.packed.second()))},
358 threshold{other.threshold} {}
359
361 ~dense_set() = default;
362
367 dense_set &operator=(const dense_set &) = default;
368
373 dense_set &operator=(dense_set &&) noexcept = default;
374
379 void swap(dense_set &other) noexcept {
380 using stl::swap;
381 swap(sparse, other.sparse);
382 swap(packed, other.packed);
383 swap(threshold, other.threshold);
384 }
385
390 [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
391 return sparse.first().get_allocator();
392 }
393
401 [[nodiscard]] const_iterator cbegin() const noexcept {
402 return packed.first().data();
403 }
404
406 [[nodiscard]] const_iterator begin() const noexcept {
407 return cbegin();
408 }
409
411 [[nodiscard]] iterator begin() noexcept {
412 return packed.first().data();
413 }
414
420 [[nodiscard]] const_iterator cend() const noexcept {
421 return packed.first().data() + packed.first().size();
422 }
423
425 [[nodiscard]] const_iterator end() const noexcept {
426 return cend();
427 }
428
430 [[nodiscard]] iterator end() noexcept {
431 return packed.first().data() + packed.first().size();
432 }
433
441 [[nodiscard]] const_reverse_iterator crbegin() const noexcept {
442 return stl::make_reverse_iterator(cend());
443 }
444
446 [[nodiscard]] const_reverse_iterator rbegin() const noexcept {
447 return crbegin();
448 }
449
451 [[nodiscard]] reverse_iterator rbegin() noexcept {
452 return stl::make_reverse_iterator(end());
453 }
454
460 [[nodiscard]] const_reverse_iterator crend() const noexcept {
461 return stl::make_reverse_iterator(cbegin());
462 }
463
465 [[nodiscard]] const_reverse_iterator rend() const noexcept {
466 return crend();
467 }
468
470 [[nodiscard]] reverse_iterator rend() noexcept {
471 return stl::make_reverse_iterator(begin());
472 }
473
478 [[nodiscard]] bool empty() const noexcept {
479 return packed.first().empty();
480 }
481
486 [[nodiscard]] size_type size() const noexcept {
487 return packed.first().size();
488 }
489
494 [[nodiscard]] size_type max_size() const noexcept {
495 return packed.first().max_size();
496 }
497
499 void clear() noexcept {
500 sparse.first().clear();
501 packed.first().clear();
502 rehash(0u);
503 }
504
512 stl::pair<iterator, bool> insert(const value_type &value) {
513 return insert_or_do_nothing(value);
514 }
515
517 stl::pair<iterator, bool> insert(value_type &&value) {
518 return insert_or_do_nothing(stl::move(value));
519 }
520
526 void insert(stl::input_iterator auto first, stl::input_iterator auto last) {
527 for(; first != last; ++first) {
528 insert(*first);
529 }
530 }
531
545 template<typename... Args>
546 stl::pair<iterator, bool> emplace(Args &&...args) {
547 if constexpr(((sizeof...(Args) == 1u) && ... && stl::is_same_v<stl::decay_t<Args>, value_type>)) {
548 return insert_or_do_nothing(stl::forward<Args>(args)...);
549 } else {
550 auto &node = packed.first().emplace_back(stl::piecewise_construct, stl::make_tuple(packed.first().size()), stl::forward_as_tuple(stl::forward<Args>(args)...));
551 const auto index = value_to_bucket(node.second);
552
553 if(auto it = constrained_find(node.second, index); it != end()) {
554 packed.first().pop_back();
555 return stl::make_pair(it, false);
556 }
557
558 stl::swap(node.first, sparse.first()[index]);
559 rehash_if_required();
560
561 return stl::make_pair(--end(), true);
562 }
563 }
564
571 const auto diff = pos - cbegin();
572 erase(*pos);
573 return begin() + diff;
574 }
575
583 const auto dist = first - cbegin();
584
585 for(auto from = last - cbegin(); from != dist; --from) {
586 erase(packed.first()[static_cast<size_type>(from) - 1u].second);
587 }
588
589 return (begin() + dist);
590 }
591
597 size_type erase(const value_type &value) {
598 for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) {
599 if(packed.second()(packed.first()[*curr].second, value)) {
600 const auto index = *curr;
601 *curr = packed.first()[*curr].first;
602 move_and_pop(index);
603 return 1u;
604 }
605 }
606
607 return 0u;
608 }
609
615 [[nodiscard]] size_type count(const value_type &key) const {
616 return find(key) != end();
617 }
618
624 [[nodiscard]] size_type count(const auto &key) const
626 return find(key) != end();
627 }
628
635 [[nodiscard]] iterator find(const value_type &value) {
636 return constrained_find(value, value_to_bucket(value));
637 }
638
640 [[nodiscard]] const_iterator find(const value_type &value) const {
641 return constrained_find(value, value_to_bucket(value));
642 }
643
650 [[nodiscard]] iterator find(const auto &value)
652 return constrained_find(value, value_to_bucket(value));
653 }
654
656 [[nodiscard]] const_iterator find(const auto &value) const
658 return constrained_find(value, value_to_bucket(value));
659 }
660
667 [[nodiscard]] stl::pair<iterator, iterator> equal_range(const value_type &value) {
668 const auto it = find(value);
669 return {it, it + !(it == end())};
670 }
671
673 [[nodiscard]] stl::pair<const_iterator, const_iterator> equal_range(const value_type &value) const {
674 const auto it = find(value);
675 return {it, it + !(it == cend())};
676 }
677
685 [[nodiscard]] stl::pair<iterator, iterator> equal_range(const auto &value)
687 const auto it = find(value);
688 return {it, it + !(it == end())};
689 }
690
692 [[nodiscard]] stl::pair<const_iterator, const_iterator> equal_range(const auto &value) const
694 const auto it = find(value);
695 return {it, it + !(it == cend())};
696 }
697
703 [[nodiscard]] bool contains(const value_type &value) const {
704 return (find(value) != cend());
705 }
706
713 [[nodiscard]] bool contains(const auto &value) const
715 return (find(value) != cend());
716 }
717
723 [[nodiscard]] const_local_iterator cbegin(const size_type index) const {
724 return {packed.first().data(), sparse.first()[index]};
725 }
726
732 [[nodiscard]] const_local_iterator begin(const size_type index) const {
733 return cbegin(index);
734 }
735
741 [[nodiscard]] local_iterator begin(const size_type index) {
742 return {packed.first().data(), sparse.first()[index]};
743 }
744
750 [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
751 return {};
752 }
753
759 [[nodiscard]] const_local_iterator end(const size_type index) const {
760 return cend(index);
761 }
762
768 [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
769 return {};
770 }
771
776 [[nodiscard]] size_type bucket_count() const {
777 return sparse.first().size();
778 }
779
784 [[nodiscard]] size_type max_bucket_count() const {
785 return sparse.first().max_size();
786 }
787
793 [[nodiscard]] size_type bucket_size(const size_type index) const {
794 return static_cast<size_type>(stl::distance(begin(index), end(index)));
795 }
796
802 [[nodiscard]] size_type bucket(const value_type &value) const {
803 return value_to_bucket(value);
804 }
805
810 [[nodiscard]] float load_factor() const {
811 return static_cast<float>(size()) / static_cast<float>(bucket_count());
812 }
813
818 [[nodiscard]] float max_load_factor() const {
819 return threshold;
820 }
821
826 void max_load_factor(const float value) {
827 ENTT_ASSERT(value > 0.f, "Invalid load factor");
828 threshold = value;
829 rehash(0u);
830 }
831
837 void rehash(const size_type cnt) {
838 auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
839 const auto cap = static_cast<size_type>(static_cast<float>(size()) / max_load_factor());
840 value = value > cap ? value : cap;
841
842 if(const auto sz = stl::bit_ceil(value); sz != bucket_count()) {
843 sparse.first().resize(sz);
844
845 for(auto &&elem: sparse.first()) {
846 elem = placeholder_position;
847 }
848
849 for(size_type pos{}, last = size(); pos < last; ++pos) {
850 const auto index = value_to_bucket(packed.first()[pos].second);
851 packed.first()[pos].first = stl::exchange(sparse.first()[index], pos);
852 }
853 }
854 }
855
861 void reserve(const size_type cnt) {
862 packed.first().reserve(cnt);
863 rehash(static_cast<size_type>(stl::ceil(static_cast<float>(cnt) / max_load_factor())));
864 }
865
870 [[nodiscard]] hasher hash_function() const {
871 return sparse.second();
872 }
873
878 [[nodiscard]] key_equal key_eq() const {
879 return packed.second();
880 }
881
882private:
885 float threshold{default_threshold};
886};
887
888} // namespace entt
889
890#endif
A compressed pair.
size_type bucket_size(const size_type index) const
Returns the number of elements in a given bucket.
void clear() noexcept
Clears the container.
stl::pair< const_iterator, const_iterator > equal_range(const auto &value) const
Returns a range containing all elements with a given value.
const_reverse_iterator crbegin() const noexcept
Returns a reverse iterator to the beginning.
const_iterator find(const auto &value) const
Finds an element with a given value.
stl::ptrdiff_t difference_type
Signed integer type.
const_iterator begin() const noexcept
Returns an iterator to the beginning.
key_equal key_eq() const
Returns the function used to compare elements for equality.
KeyEqual key_equal
Type of function to use to compare the elements for equality.
const_iterator cend() const noexcept
Returns an iterator to the end.
internal::dense_set_iterator< typename packed_container_type::pointer > iterator
Random access iterator type.
iterator erase(const_iterator first, const_iterator last)
Removes the given elements from a container.
Hash hasher
Type of function to use to hash the elements.
size_type bucket(const value_type &value) const
Returns the bucket for a given element.
reverse_iterator rbegin() noexcept
Returns a reverse iterator to the beginning.
iterator find(const value_type &value)
Finds an element with a given value.
Type key_type
Key type of the container.
Type value_type
Value type of the container.
internal::dense_set_iterator< typename packed_container_type::const_pointer > const_iterator
Constant random access iterator type.
dense_set & operator=(dense_set &&) noexcept=default
Default move assignment operator.
size_type bucket_count() const
Returns the number of buckets.
size_type max_size() const noexcept
Returns the maximum possible number of elements.
size_type size() const noexcept
Returns the number of elements in a container.
void insert(stl::input_iterator auto first, stl::input_iterator auto last)
Inserts elements into the container, if they do not exist.
local_iterator end(const size_type index)
Returns an iterator to the end of a given bucket.
dense_set(const dense_set &other, const allocator_type &allocator)
Allocator-extended copy constructor.
bool empty() const noexcept
Checks whether a container is empty.
stl::reverse_iterator< const_iterator > const_reverse_iterator
Constant reverse iterator type.
dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator)
Constructs an empty container with a given allocator, hash function and user supplied minimal number ...
internal::dense_set_local_iterator< typename packed_container_type::pointer > local_iterator
Forward iterator type.
const_iterator find(const value_type &value) const
Finds an element with a given value.
const_iterator cbegin() const noexcept
Returns an iterator to the beginning.
const_reverse_iterator rend() const noexcept
Returns a reverse iterator to the end.
stl::size_t size_type
Unsigned integer type.
dense_set(const size_type cnt, const allocator_type &allocator)
Constructs an empty container with a given allocator and user supplied minimal number of buckets.
iterator find(const auto &value)
Finds an element that compares equivalent to a given value.
hasher hash_function() const
Returns the function used to hash the elements.
stl::pair< iterator, bool > insert(value_type &&value)
Inserts an element into the container, if it does not exist.
~dense_set()=default
Default destructor.
void reserve(const size_type cnt)
Reserves space for at least the specified number of elements and regenerates the hash table.
const_local_iterator cend(const size_type index) const
Returns an iterator to the end of a given bucket.
Allocator allocator_type
Allocator type.
size_type count(const value_type &key) const
Returns the number of elements matching a value (either 1 or 0).
iterator end() noexcept
Returns an iterator to the end.
internal::dense_set_local_iterator< typename packed_container_type::const_pointer > const_local_iterator
Constant forward iterator type.
stl::pair< iterator, bool > insert(const value_type &value)
Inserts an element into the container, if it does not exist.
dense_set(const dense_set &)=default
Default copy constructor.
stl::pair< iterator, bool > emplace(Args &&...args)
Constructs an element in-place, if it does not exist.
bool contains(const auto &value) const
Checks if the container contains an element that compares equivalent to a given value.
dense_set(dense_set &&) noexcept=default
Default move constructor.
const_local_iterator end(const size_type index) const
Returns an iterator to the end of a given bucket.
void max_load_factor(const float value)
Sets the desired maximum average number of elements per bucket.
float load_factor() const
Returns the average number of elements per bucket.
size_type count(const auto &key) const
Returns the number of elements matching a key (either 1 or 0).
size_type erase(const value_type &value)
Removes the element associated with a given value.
stl::reverse_iterator< iterator > reverse_iterator
Reverse iterator type.
size_type max_bucket_count() const
Returns the maximum number of buckets.
dense_set(const allocator_type &allocator)
Constructs an empty container with a given allocator.
const_reverse_iterator rbegin() const noexcept
Returns a reverse iterator to the beginning.
dense_set()
Default constructor.
const_iterator end() const noexcept
Returns an iterator to the end.
dense_set & operator=(const dense_set &)=default
Default copy assignment operator.
dense_set(const size_type cnt, const hasher &hash=hasher{}, const key_equal &equal=key_equal{}, const allocator_type &allocator=allocator_type{})
Constructs an empty container with a given allocator, hash function, compare function and user suppli...
iterator erase(const_iterator pos)
Removes an element from a given position.
void rehash(const size_type cnt)
Reserves at least the specified number of buckets and regenerates the hash table.
const_reverse_iterator crend() const noexcept
Returns a reverse iterator to the end.
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
stl::pair< const_iterator, const_iterator > equal_range(const value_type &value) const
Returns a range containing all elements with a given value.
stl::pair< iterator, iterator > equal_range(const auto &value)
Returns a range containing all elements that compare equivalent to a given value.
reverse_iterator rend() noexcept
Returns a reverse iterator to the end.
float max_load_factor() const
Returns the maximum average number of elements per bucket.
const_local_iterator begin(const size_type index) const
Returns an iterator to the beginning of a given bucket.
local_iterator begin(const size_type index)
Returns an iterator to the beginning of a given bucket.
const_local_iterator cbegin(const size_type index) const
Returns an iterator to the beginning of a given bucket.
stl::pair< iterator, iterator > equal_range(const value_type &value)
Returns a range containing all elements with a given value.
bool contains(const value_type &value) const
Checks if the container contains an element with a given value.
iterator begin() noexcept
Returns an iterator to the beginning.
Custom EnTT namespace for the standard template library.
Definition entt.hpp:5
EnTT default namespace.
Definition dense_map.hpp:25
constexpr bool is_transparent_v
Helper variable template.
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
constexpr Type fast_mod(const Type value, const stl::size_t mod) noexcept
Fast module utility function (powers of two only).
Definition bit.hpp:19