149e08aacStbbdev /*
2*3fbdbb8bSkboyarinov     Copyright (c) 2005-2022 Intel Corporation
349e08aacStbbdev 
449e08aacStbbdev     Licensed under the Apache License, Version 2.0 (the "License");
549e08aacStbbdev     you may not use this file except in compliance with the License.
649e08aacStbbdev     You may obtain a copy of the License at
749e08aacStbbdev 
849e08aacStbbdev         http://www.apache.org/licenses/LICENSE-2.0
949e08aacStbbdev 
1049e08aacStbbdev     Unless required by applicable law or agreed to in writing, software
1149e08aacStbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1249e08aacStbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1349e08aacStbbdev     See the License for the specific language governing permissions and
1449e08aacStbbdev     limitations under the License.
1549e08aacStbbdev */
1649e08aacStbbdev 
1749e08aacStbbdev #ifndef __TBB_concurrent_unordered_map_H
1849e08aacStbbdev #define __TBB_concurrent_unordered_map_H
1949e08aacStbbdev 
2049e08aacStbbdev #include "detail/_namespace_injection.h"
2149e08aacStbbdev #include "detail/_concurrent_unordered_base.h"
2249e08aacStbbdev #include "tbb_allocator.h"
2349e08aacStbbdev #include <functional>
2449e08aacStbbdev 
2549e08aacStbbdev namespace tbb {
2649e08aacStbbdev namespace detail {
2749e08aacStbbdev namespace d1 {
2849e08aacStbbdev 
2949e08aacStbbdev template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator, bool AllowMultimapping>
3049e08aacStbbdev struct concurrent_unordered_map_traits {
3149e08aacStbbdev     using value_type = std::pair<const Key, T>;
3249e08aacStbbdev     using key_type = Key;
3349e08aacStbbdev     using allocator_type = Allocator;
3449e08aacStbbdev     using hash_compare_type = hash_compare<Key, Hash, KeyEqual>;
3549e08aacStbbdev     static constexpr bool allow_multimapping = AllowMultimapping;
3649e08aacStbbdev 
get_keyconcurrent_unordered_map_traits3749e08aacStbbdev     static constexpr const key_type& get_key( const value_type& value ) {
3849e08aacStbbdev         return value.first;
3949e08aacStbbdev     }
4049e08aacStbbdev }; // struct concurrent_unordered_map_traits
4149e08aacStbbdev 
4249e08aacStbbdev template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
4349e08aacStbbdev class concurrent_unordered_multimap;
4449e08aacStbbdev 
4549e08aacStbbdev template <typename Key, typename T, typename Hash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>,
4649e08aacStbbdev           typename Allocator = tbb::tbb_allocator<std::pair<const Key, T>> >
4749e08aacStbbdev class concurrent_unordered_map
4849e08aacStbbdev     : public concurrent_unordered_base<concurrent_unordered_map_traits<Key, T, Hash, KeyEqual, Allocator, false>>
4949e08aacStbbdev {
5049e08aacStbbdev     using traits_type = concurrent_unordered_map_traits<Key, T, Hash, KeyEqual, Allocator, false>;
5149e08aacStbbdev     using base_type = concurrent_unordered_base<traits_type>;
5249e08aacStbbdev public:
5349e08aacStbbdev     using key_type = typename base_type::key_type;
5449e08aacStbbdev     using mapped_type = T;
5549e08aacStbbdev     using value_type = typename base_type::value_type;
5649e08aacStbbdev     using size_type = typename base_type::size_type;
5749e08aacStbbdev     using difference_type = typename base_type::difference_type;
5849e08aacStbbdev     using hasher = typename base_type::hasher;
5949e08aacStbbdev     using key_equal = typename base_type::key_equal;
6049e08aacStbbdev     using allocator_type = typename base_type::allocator_type;
6149e08aacStbbdev     using reference = typename base_type::reference;
6249e08aacStbbdev     using const_reference = typename base_type::const_reference;
6349e08aacStbbdev     using pointer = typename base_type::pointer;
6449e08aacStbbdev     using const_pointer = typename base_type::const_pointer;
6549e08aacStbbdev     using iterator = typename base_type::iterator;
6649e08aacStbbdev     using const_iterator = typename base_type::const_iterator;
6749e08aacStbbdev     using local_iterator = typename base_type::local_iterator;
6849e08aacStbbdev     using const_local_iterator = typename base_type::const_local_iterator;
6949e08aacStbbdev     using node_type = typename base_type::node_type;
7049e08aacStbbdev 
7149e08aacStbbdev     // Include constructors of base type
7249e08aacStbbdev     using base_type::base_type;
7349e08aacStbbdev 
74d86ed7fbStbbdev     // Required for implicit deduction guides
75d86ed7fbStbbdev     concurrent_unordered_map() = default;
76d86ed7fbStbbdev     concurrent_unordered_map( const concurrent_unordered_map& ) = default;
concurrent_unordered_map(const concurrent_unordered_map & other,const allocator_type & alloc)77d86ed7fbStbbdev     concurrent_unordered_map( const concurrent_unordered_map& other, const allocator_type& alloc ) : base_type(other, alloc) {}
78d86ed7fbStbbdev     concurrent_unordered_map( concurrent_unordered_map&& ) = default;
concurrent_unordered_map(concurrent_unordered_map && other,const allocator_type & alloc)79d86ed7fbStbbdev     concurrent_unordered_map( concurrent_unordered_map&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {}
80d86ed7fbStbbdev     // Required to respect the rule of 5
81d86ed7fbStbbdev     concurrent_unordered_map& operator=( const concurrent_unordered_map& ) = default;
82d86ed7fbStbbdev     concurrent_unordered_map& operator=( concurrent_unordered_map&& ) = default;
83d86ed7fbStbbdev 
842713d41eSIlya Isaev     concurrent_unordered_map& operator=( std::initializer_list<value_type> il ) {
852713d41eSIlya Isaev         base_type::operator= (il);
862713d41eSIlya Isaev         return *this;
872713d41eSIlya Isaev     }
882713d41eSIlya Isaev 
8949e08aacStbbdev     // Observers
9049e08aacStbbdev     mapped_type& operator[]( const key_type& key ) {
9149e08aacStbbdev         iterator where = this->find(key);
9249e08aacStbbdev 
9349e08aacStbbdev         if (where == this->end()) {
9449e08aacStbbdev             where = this->emplace(std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>()).first;
9549e08aacStbbdev         }
9649e08aacStbbdev         return where->second;
9749e08aacStbbdev     }
9849e08aacStbbdev 
9949e08aacStbbdev     mapped_type& operator[]( key_type&& key ) {
10049e08aacStbbdev         iterator where = this->find(key);
10149e08aacStbbdev 
10249e08aacStbbdev         if (where == this->end()) {
10349e08aacStbbdev             where = this->emplace(std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple<>()).first;
10449e08aacStbbdev         }
10549e08aacStbbdev         return where->second;
10649e08aacStbbdev     }
10749e08aacStbbdev 
at(const key_type & key)10849e08aacStbbdev     mapped_type& at( const key_type& key ) {
10949e08aacStbbdev         iterator where = this->find(key);
11049e08aacStbbdev 
11149e08aacStbbdev         if (where == this->end()) {
11249e08aacStbbdev             throw_exception(exception_id::invalid_key);
11349e08aacStbbdev         }
11449e08aacStbbdev         return where->second;
11549e08aacStbbdev     }
11649e08aacStbbdev 
at(const key_type & key)11749e08aacStbbdev     const mapped_type& at( const key_type& key ) const {
11849e08aacStbbdev         const_iterator where = this->find(key);
11949e08aacStbbdev 
12049e08aacStbbdev         if (where == this->end()) {
12149e08aacStbbdev             throw_exception(exception_id::out_of_range);
12249e08aacStbbdev         }
12349e08aacStbbdev         return where->second;
12449e08aacStbbdev     }
12549e08aacStbbdev 
12649e08aacStbbdev     using base_type::insert;
12749e08aacStbbdev 
12849e08aacStbbdev     template<typename P>
12949e08aacStbbdev     typename std::enable_if<std::is_constructible<value_type, P&&>::value,
insert(P && value)13049e08aacStbbdev                             std::pair<iterator, bool>>::type insert( P&& value ) {
13149e08aacStbbdev         return this->emplace(std::forward<P>(value));
13249e08aacStbbdev     }
13349e08aacStbbdev 
13449e08aacStbbdev     template<typename P>
13549e08aacStbbdev     typename std::enable_if<std::is_constructible<value_type, P&&>::value,
insert(const_iterator hint,P && value)13649e08aacStbbdev                             iterator>::type insert( const_iterator hint, P&& value ) {
13749e08aacStbbdev         return this->emplace_hint(hint, std::forward<P>(value));
13849e08aacStbbdev     }
13949e08aacStbbdev 
14049e08aacStbbdev     template <typename OtherHash, typename OtherKeyEqual>
merge(concurrent_unordered_map<key_type,mapped_type,OtherHash,OtherKeyEqual,allocator_type> & source)14149e08aacStbbdev     void merge( concurrent_unordered_map<key_type, mapped_type, OtherHash, OtherKeyEqual, allocator_type>& source ) {
14249e08aacStbbdev         this->internal_merge(source);
14349e08aacStbbdev     }
14449e08aacStbbdev 
14549e08aacStbbdev     template <typename OtherHash, typename OtherKeyEqual>
merge(concurrent_unordered_map<key_type,mapped_type,OtherHash,OtherKeyEqual,allocator_type> && source)14649e08aacStbbdev     void merge( concurrent_unordered_map<key_type, mapped_type, OtherHash, OtherKeyEqual, allocator_type>&& source ) {
14749e08aacStbbdev         this->internal_merge(std::move(source));
14849e08aacStbbdev     }
14949e08aacStbbdev 
15049e08aacStbbdev     template <typename OtherHash, typename OtherKeyEqual>
merge(concurrent_unordered_multimap<key_type,mapped_type,OtherHash,OtherKeyEqual,allocator_type> & source)15149e08aacStbbdev     void merge( concurrent_unordered_multimap<key_type, mapped_type, OtherHash, OtherKeyEqual, allocator_type>& source ) {
15249e08aacStbbdev         this->internal_merge(source);
15349e08aacStbbdev     }
15449e08aacStbbdev 
15549e08aacStbbdev     template <typename OtherHash, typename OtherKeyEqual>
merge(concurrent_unordered_multimap<key_type,mapped_type,OtherHash,OtherKeyEqual,allocator_type> && source)15649e08aacStbbdev     void merge( concurrent_unordered_multimap<key_type, mapped_type, OtherHash, OtherKeyEqual, allocator_type>&& source ) {
15749e08aacStbbdev         this->internal_merge(std::move(source));
15849e08aacStbbdev     }
15949e08aacStbbdev }; // class concurrent_unordered_map
16049e08aacStbbdev 
16149e08aacStbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
162d86ed7fbStbbdev template <typename It,
163d86ed7fbStbbdev           typename Hash = std::hash<iterator_key_t<It>>,
164d86ed7fbStbbdev           typename KeyEq = std::equal_to<iterator_key_t<It>>,
165d86ed7fbStbbdev           typename Alloc = tbb::tbb_allocator<iterator_alloc_pair_t<It>>,
166d86ed7fbStbbdev           typename = std::enable_if_t<is_input_iterator_v<It>>,
167d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>,
168d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<Hash>>,
169d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<KeyEq>>,
170d86ed7fbStbbdev           typename = std::enable_if_t<!std::is_integral_v<Hash>>>
171d86ed7fbStbbdev concurrent_unordered_map( It, It, std::size_t =  {},
172d86ed7fbStbbdev                           Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() )
173d86ed7fbStbbdev -> concurrent_unordered_map<iterator_key_t<It>, iterator_mapped_t<It>, Hash, KeyEq, Alloc>;
17449e08aacStbbdev 
175d86ed7fbStbbdev template <typename Key, typename T,
176d86ed7fbStbbdev           typename Hash = std::hash<std::remove_const_t<Key>>,
177d86ed7fbStbbdev           typename KeyEq = std::equal_to<std::remove_const_t<Key>>,
178d86ed7fbStbbdev           typename Alloc = tbb::tbb_allocator<std::pair<const Key, T>>,
179d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>,
180d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<Hash>>,
181d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<KeyEq>>,
182d86ed7fbStbbdev           typename = std::enable_if_t<!std::is_integral_v<Hash>>>
183d86ed7fbStbbdev concurrent_unordered_map( std::initializer_list<std::pair<Key, T>>, std::size_t = {},
184d86ed7fbStbbdev                           Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() )
185d86ed7fbStbbdev -> concurrent_unordered_map<std::remove_const_t<Key>, T, Hash, KeyEq, Alloc>;
18649e08aacStbbdev 
187d86ed7fbStbbdev template <typename It, typename Alloc,
188d86ed7fbStbbdev           typename = std::enable_if_t<is_input_iterator_v<It>>,
189d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>>
190d86ed7fbStbbdev concurrent_unordered_map( It, It, std::size_t, Alloc )
191d86ed7fbStbbdev -> concurrent_unordered_map<iterator_key_t<It>, iterator_mapped_t<It>,
192d86ed7fbStbbdev                             std::hash<iterator_key_t<It>>,
193d86ed7fbStbbdev                             std::equal_to<iterator_key_t<It>>, Alloc>;
19449e08aacStbbdev 
195d86ed7fbStbbdev // TODO: investigate if a deduction guide for concurrent_unordered_map(It, It, Alloc) is needed
19649e08aacStbbdev 
197d86ed7fbStbbdev template <typename It, typename Hash, typename Alloc,
198d86ed7fbStbbdev           typename = std::enable_if_t<is_input_iterator_v<It>>,
199d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>,
200d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<Hash>>,
201d86ed7fbStbbdev           typename = std::enable_if_t<!std::is_integral_v<Hash>>>
202d86ed7fbStbbdev concurrent_unordered_map( It, It, std::size_t, Hash, Alloc )
203d86ed7fbStbbdev -> concurrent_unordered_map<iterator_key_t<It>, iterator_mapped_t<It>,
204d86ed7fbStbbdev                             Hash, std::equal_to<iterator_key_t<It>>, Alloc>;
20549e08aacStbbdev 
206d86ed7fbStbbdev template <typename Key, typename T, typename Alloc,
207d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>>
208d86ed7fbStbbdev concurrent_unordered_map( std::initializer_list<std::pair<Key, T>>, std::size_t, Alloc )
209d86ed7fbStbbdev -> concurrent_unordered_map<std::remove_const_t<Key>, T, std::hash<std::remove_const_t<Key>>,
210d86ed7fbStbbdev                             std::equal_to<std::remove_const_t<Key>>, Alloc>;
21149e08aacStbbdev 
212d86ed7fbStbbdev template <typename Key, typename T, typename Alloc,
213d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>>
214d86ed7fbStbbdev concurrent_unordered_map( std::initializer_list<std::pair<Key, T>>, Alloc )
215d86ed7fbStbbdev -> concurrent_unordered_map<std::remove_const_t<Key>, T, std::hash<std::remove_const_t<Key>>,
216d86ed7fbStbbdev                             std::equal_to<std::remove_const_t<Key>>, Alloc>;
21749e08aacStbbdev 
218d86ed7fbStbbdev template <typename Key, typename T, typename Hash, typename Alloc,
219d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>,
220d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<Hash>>,
221d86ed7fbStbbdev           typename = std::enable_if_t<!std::is_integral_v<Hash>>>
222d86ed7fbStbbdev concurrent_unordered_map( std::initializer_list<std::pair<Key, T>>, std::size_t, Hash, Alloc )
223d86ed7fbStbbdev -> concurrent_unordered_map<std::remove_const_t<Key>, T, Hash,
224d86ed7fbStbbdev                             std::equal_to<std::remove_const_t<Key>>, Alloc>;
22549e08aacStbbdev 
226*3fbdbb8bSkboyarinov #if __APPLE__ && __TBB_CLANG_VERSION == 100000
227*3fbdbb8bSkboyarinov // An explicit deduction guide is required for copy/move constructor with allocator for APPLE LLVM 10.0.0
228*3fbdbb8bSkboyarinov // due to an issue with generating an implicit deduction guide for these constructors under several strange surcumstances.
229*3fbdbb8bSkboyarinov // Currently the issue takes place because the last template parameter for Traits is boolean, it should not affect the deduction guides
230*3fbdbb8bSkboyarinov // The issue reproduces only on this version of the compiler
231*3fbdbb8bSkboyarinov template <typename Key, typename T, typename Hash, typename KeyEq, typename Alloc>
232*3fbdbb8bSkboyarinov concurrent_unordered_map( concurrent_unordered_map<Key, T, Hash, KeyEq, Alloc>, Alloc )
233*3fbdbb8bSkboyarinov -> concurrent_unordered_map<Key, T, Hash, KeyEq, Alloc>;
234*3fbdbb8bSkboyarinov #endif
235*3fbdbb8bSkboyarinov 
23649e08aacStbbdev #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
23749e08aacStbbdev 
23849e08aacStbbdev template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
swap(concurrent_unordered_map<Key,T,Hash,KeyEqual,Allocator> & lhs,concurrent_unordered_map<Key,T,Hash,KeyEqual,Allocator> & rhs)23949e08aacStbbdev void swap( concurrent_unordered_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
24049e08aacStbbdev            concurrent_unordered_map<Key, T, Hash, KeyEqual, Allocator>& rhs ) {
24149e08aacStbbdev     lhs.swap(rhs);
24249e08aacStbbdev }
24349e08aacStbbdev 
24449e08aacStbbdev template <typename Key, typename T, typename Hash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>,
24549e08aacStbbdev           typename Allocator = tbb::tbb_allocator<std::pair<const Key, T>> >
24649e08aacStbbdev class concurrent_unordered_multimap
24749e08aacStbbdev     : public concurrent_unordered_base<concurrent_unordered_map_traits<Key, T, Hash, KeyEqual, Allocator, true>>
24849e08aacStbbdev {
24949e08aacStbbdev     using traits_type = concurrent_unordered_map_traits<Key, T, Hash, KeyEqual, Allocator, true>;
25049e08aacStbbdev     using base_type = concurrent_unordered_base<traits_type>;
25149e08aacStbbdev public:
25249e08aacStbbdev     using key_type = typename base_type::key_type;
25349e08aacStbbdev     using mapped_type = T;
25449e08aacStbbdev     using value_type = typename base_type::value_type;
25549e08aacStbbdev     using size_type = typename base_type::size_type;
25649e08aacStbbdev     using difference_type = typename base_type::difference_type;
25749e08aacStbbdev     using hasher = typename base_type::hasher;
25849e08aacStbbdev     using key_equal = typename base_type::key_equal;
25949e08aacStbbdev     using allocator_type = typename base_type::allocator_type;
26049e08aacStbbdev     using reference = typename base_type::reference;
26149e08aacStbbdev     using const_reference = typename base_type::const_reference;
26249e08aacStbbdev     using pointer = typename base_type::pointer;
26349e08aacStbbdev     using const_pointer = typename base_type::const_pointer;
26449e08aacStbbdev     using iterator = typename base_type::iterator;
26549e08aacStbbdev     using const_iterator = typename base_type::const_iterator;
26649e08aacStbbdev     using local_iterator = typename base_type::local_iterator;
26749e08aacStbbdev     using const_local_iterator = typename base_type::const_local_iterator;
26849e08aacStbbdev     using node_type = typename base_type::node_type;
26949e08aacStbbdev 
27049e08aacStbbdev     // Include constructors of base type
27149e08aacStbbdev     using base_type::base_type;
27249e08aacStbbdev     using base_type::insert;
27349e08aacStbbdev 
274d86ed7fbStbbdev     // Required for implicit deduction guides
275d86ed7fbStbbdev     concurrent_unordered_multimap() = default;
276d86ed7fbStbbdev     concurrent_unordered_multimap( const concurrent_unordered_multimap& ) = default;
concurrent_unordered_multimap(const concurrent_unordered_multimap & other,const allocator_type & alloc)277d86ed7fbStbbdev     concurrent_unordered_multimap( const concurrent_unordered_multimap& other, const allocator_type& alloc ) : base_type(other, alloc) {}
278d86ed7fbStbbdev     concurrent_unordered_multimap( concurrent_unordered_multimap&& ) = default;
concurrent_unordered_multimap(concurrent_unordered_multimap && other,const allocator_type & alloc)279d86ed7fbStbbdev     concurrent_unordered_multimap( concurrent_unordered_multimap&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {}
280d86ed7fbStbbdev     // Required to respect the rule of 5
281d86ed7fbStbbdev     concurrent_unordered_multimap& operator=( const concurrent_unordered_multimap& ) = default;
282d86ed7fbStbbdev     concurrent_unordered_multimap& operator=( concurrent_unordered_multimap&& ) = default;
283d86ed7fbStbbdev 
2842713d41eSIlya Isaev     concurrent_unordered_multimap& operator=( std::initializer_list<value_type> il ) {
2852713d41eSIlya Isaev         base_type::operator= (il);
2862713d41eSIlya Isaev         return *this;
2872713d41eSIlya Isaev     }
2882713d41eSIlya Isaev 
28949e08aacStbbdev     template <typename P>
29049e08aacStbbdev     typename std::enable_if<std::is_constructible<value_type, P&&>::value,
insert(P && value)29149e08aacStbbdev                             std::pair<iterator, bool>>::type insert( P&& value ) {
29249e08aacStbbdev         return this->emplace(std::forward<P>(value));
29349e08aacStbbdev     }
29449e08aacStbbdev 
29549e08aacStbbdev     template<typename P>
29649e08aacStbbdev     typename std::enable_if<std::is_constructible<value_type, P&&>::value,
insert(const_iterator hint,P && value)29749e08aacStbbdev                             iterator>::type insert( const_iterator hint, P&& value ) {
29849e08aacStbbdev         return this->emplace_hint(hint, std::forward<P&&>(value));
29949e08aacStbbdev     }
30049e08aacStbbdev 
30149e08aacStbbdev     template <typename OtherHash, typename OtherKeyEqual>
merge(concurrent_unordered_map<key_type,mapped_type,OtherHash,OtherKeyEqual,allocator_type> & source)30249e08aacStbbdev     void merge( concurrent_unordered_map<key_type, mapped_type, OtherHash, OtherKeyEqual, allocator_type>& source ) {
30349e08aacStbbdev         this->internal_merge(source);
30449e08aacStbbdev     }
30549e08aacStbbdev 
30649e08aacStbbdev     template <typename OtherHash, typename OtherKeyEqual>
merge(concurrent_unordered_map<key_type,mapped_type,OtherHash,OtherKeyEqual,allocator_type> && source)30749e08aacStbbdev     void merge( concurrent_unordered_map<key_type, mapped_type, OtherHash, OtherKeyEqual, allocator_type>&& source ) {
30849e08aacStbbdev         this->internal_merge(std::move(source));
30949e08aacStbbdev     }
31049e08aacStbbdev 
31149e08aacStbbdev     template <typename OtherHash, typename OtherKeyEqual>
merge(concurrent_unordered_multimap<key_type,mapped_type,OtherHash,OtherKeyEqual,allocator_type> & source)31249e08aacStbbdev     void merge( concurrent_unordered_multimap<key_type, mapped_type, OtherHash, OtherKeyEqual, allocator_type>& source ) {
31349e08aacStbbdev         this->internal_merge(source);
31449e08aacStbbdev     }
31549e08aacStbbdev 
31649e08aacStbbdev     template <typename OtherHash, typename OtherKeyEqual>
merge(concurrent_unordered_multimap<key_type,mapped_type,OtherHash,OtherKeyEqual,allocator_type> && source)31749e08aacStbbdev     void merge( concurrent_unordered_multimap<key_type, mapped_type, OtherHash, OtherKeyEqual, allocator_type>&& source ) {
31849e08aacStbbdev         this->internal_merge(std::move(source));
31949e08aacStbbdev     }
32049e08aacStbbdev }; // class concurrent_unordered_multimap
32149e08aacStbbdev 
32249e08aacStbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
32349e08aacStbbdev 
324d86ed7fbStbbdev template <typename It,
325d86ed7fbStbbdev           typename Hash = std::hash<iterator_key_t<It>>,
326d86ed7fbStbbdev           typename KeyEq = std::equal_to<iterator_key_t<It>>,
327d86ed7fbStbbdev           typename Alloc = tbb::tbb_allocator<iterator_alloc_pair_t<It>>,
328d86ed7fbStbbdev           typename = std::enable_if_t<is_input_iterator_v<It>>,
329d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>,
330d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<Hash>>,
331d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<KeyEq>>,
332d86ed7fbStbbdev           typename = std::enable_if_t<!std::is_integral_v<Hash>>>
333d86ed7fbStbbdev concurrent_unordered_multimap( It, It, std::size_t = {}, Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() )
334d86ed7fbStbbdev -> concurrent_unordered_multimap<iterator_key_t<It>, iterator_mapped_t<It>, Hash, KeyEq, Alloc>;
33549e08aacStbbdev 
336d86ed7fbStbbdev template <typename Key, typename T,
337d86ed7fbStbbdev           typename Hash = std::hash<std::remove_const_t<Key>>,
338d86ed7fbStbbdev           typename KeyEq = std::equal_to<std::remove_const_t<Key>>,
339d86ed7fbStbbdev           typename Alloc = tbb::tbb_allocator<std::pair<const Key, T>>,
340d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>,
341d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<Hash>>,
342d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<KeyEq>>,
343d86ed7fbStbbdev           typename = std::enable_if_t<!std::is_integral_v<Hash>>>
344d86ed7fbStbbdev concurrent_unordered_multimap( std::initializer_list<std::pair<Key, T>>, std::size_t = {},
345d86ed7fbStbbdev                                Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() )
346d86ed7fbStbbdev -> concurrent_unordered_multimap<std::remove_const_t<Key>, T, Hash, KeyEq, Alloc>;
34749e08aacStbbdev 
348d86ed7fbStbbdev template <typename It, typename Alloc,
349d86ed7fbStbbdev           typename = std::enable_if_t<is_input_iterator_v<It>>,
350d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>>
351d86ed7fbStbbdev concurrent_unordered_multimap( It, It, std::size_t, Alloc )
352d86ed7fbStbbdev -> concurrent_unordered_multimap<iterator_key_t<It>, iterator_mapped_t<It>,
353d86ed7fbStbbdev                                  std::hash<iterator_key_t<It>>,
354d86ed7fbStbbdev                                  std::equal_to<iterator_key_t<It>>, Alloc>;
35549e08aacStbbdev 
356d86ed7fbStbbdev template <typename It, typename Hash, typename Alloc,
357d86ed7fbStbbdev           typename = std::enable_if_t<is_input_iterator_v<It>>,
358d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>,
359d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<Hash>>,
360d86ed7fbStbbdev           typename = std::enable_if_t<!std::is_integral_v<Hash>>>
361d86ed7fbStbbdev concurrent_unordered_multimap( It, It, std::size_t, Hash, Alloc )
362d86ed7fbStbbdev -> concurrent_unordered_multimap<iterator_key_t<It>, iterator_mapped_t<It>, Hash,
363d86ed7fbStbbdev                                  std::equal_to<iterator_key_t<It>>, Alloc>;
36449e08aacStbbdev 
365d86ed7fbStbbdev template <typename Key, typename T, typename Alloc,
366d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>>
367d86ed7fbStbbdev concurrent_unordered_multimap( std::initializer_list<std::pair<Key, T>>, std::size_t, Alloc )
368d86ed7fbStbbdev -> concurrent_unordered_multimap<std::remove_const_t<Key>, T, std::hash<std::remove_const_t<Key>>,
369d86ed7fbStbbdev                                  std::equal_to<std::remove_const_t<Key>>, Alloc>;
370d86ed7fbStbbdev 
371d86ed7fbStbbdev template <typename Key, typename T, typename Alloc,
372d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>>
373d86ed7fbStbbdev concurrent_unordered_multimap( std::initializer_list<std::pair<Key, T>>, Alloc )
374d86ed7fbStbbdev -> concurrent_unordered_multimap<std::remove_const_t<Key>, T, std::hash<std::remove_const_t<Key>>,
375d86ed7fbStbbdev                                  std::equal_to<std::remove_const_t<Key>>, Alloc>;
376d86ed7fbStbbdev 
377d86ed7fbStbbdev template <typename Key, typename T, typename Hash, typename Alloc,
378d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>,
379d86ed7fbStbbdev           typename = std::enable_if_t<!is_allocator_v<Hash>>,
380d86ed7fbStbbdev           typename = std::enable_if_t<!std::is_integral_v<Hash>>>
381d86ed7fbStbbdev concurrent_unordered_multimap( std::initializer_list<std::pair<Key, T>>, std::size_t, Hash, Alloc )
382d86ed7fbStbbdev -> concurrent_unordered_multimap<std::remove_const_t<Key>, T, Hash,
383d86ed7fbStbbdev                                  std::equal_to<std::remove_const_t<Key>>, Alloc>;
38449e08aacStbbdev 
385*3fbdbb8bSkboyarinov #if __APPLE__ && __TBB_CLANG_VERSION == 100000
386*3fbdbb8bSkboyarinov // An explicit deduction guide is required for copy/move constructor with allocator for APPLE LLVM 10.0.0
387*3fbdbb8bSkboyarinov // due to an issue with generating an implicit deduction guide for these constructors under several strange surcumstances.
388*3fbdbb8bSkboyarinov // Currently the issue takes place because the last template parameter for Traits is boolean, it should not affect the deduction guides
389*3fbdbb8bSkboyarinov // The issue reproduces only on this version of the compiler
390*3fbdbb8bSkboyarinov template <typename Key, typename T, typename Hash, typename KeyEq, typename Alloc>
391*3fbdbb8bSkboyarinov concurrent_unordered_multimap( concurrent_unordered_multimap<Key, T, Hash, KeyEq, Alloc>, Alloc )
392*3fbdbb8bSkboyarinov -> concurrent_unordered_multimap<Key, T, Hash, KeyEq, Alloc>;
393*3fbdbb8bSkboyarinov #endif
39449e08aacStbbdev #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
39549e08aacStbbdev 
39649e08aacStbbdev template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
swap(concurrent_unordered_multimap<Key,T,Hash,KeyEqual,Allocator> & lhs,concurrent_unordered_multimap<Key,T,Hash,KeyEqual,Allocator> & rhs)39749e08aacStbbdev void swap( concurrent_unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& lhs,
39849e08aacStbbdev            concurrent_unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& rhs ) {
39949e08aacStbbdev     lhs.swap(rhs);
40049e08aacStbbdev }
40149e08aacStbbdev 
40249e08aacStbbdev } // namespace d1
40349e08aacStbbdev } // namespace detail
40449e08aacStbbdev 
40549e08aacStbbdev inline namespace v1 {
40649e08aacStbbdev 
40749e08aacStbbdev using detail::d1::concurrent_unordered_map;
40849e08aacStbbdev using detail::d1::concurrent_unordered_multimap;
40949e08aacStbbdev using detail::split;
41049e08aacStbbdev 
41149e08aacStbbdev } // inline namespace v1
41249e08aacStbbdev } // namespace tbb
41349e08aacStbbdev 
41449e08aacStbbdev #endif // __TBB_concurrent_unordered_map_H
415