Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:52:34

0001 /* Fast open-addressing, node-based concurrent hashmap.
0002  *
0003  * Copyright 2023 Christian Mazakas.
0004  * Copyright 2023-2024 Joaquin M Lopez Munoz.
0005  * Distributed under the Boost Software License, Version 1.0.
0006  * (See accompanying file LICENSE_1_0.txt or copy at
0007  * http://www.boost.org/LICENSE_1_0.txt)
0008  *
0009  * See https://www.boost.org/libs/unordered for library home page.
0010  */
0011 
0012 #ifndef BOOST_UNORDERED_CONCURRENT_NODE_MAP_HPP
0013 #define BOOST_UNORDERED_CONCURRENT_NODE_MAP_HPP
0014 
0015 #include <boost/unordered/concurrent_node_map_fwd.hpp>
0016 #include <boost/unordered/detail/concurrent_static_asserts.hpp>
0017 #include <boost/unordered/detail/foa/concurrent_table.hpp>
0018 #include <boost/unordered/detail/foa/element_type.hpp>
0019 #include <boost/unordered/detail/foa/node_map_handle.hpp>
0020 #include <boost/unordered/detail/foa/node_map_types.hpp>
0021 #include <boost/unordered/detail/type_traits.hpp>
0022 #include <boost/unordered/unordered_node_map_fwd.hpp>
0023 
0024 #include <boost/container_hash/hash.hpp>
0025 #include <boost/core/allocator_access.hpp>
0026 #include <boost/core/serialization.hpp>
0027 
0028 #include <type_traits>
0029 
0030 namespace boost {
0031   namespace unordered {
0032     template <class Key, class T, class Hash, class Pred, class Allocator>
0033     class concurrent_node_map
0034     {
0035     private:
0036       template <class Key2, class T2, class Hash2, class Pred2,
0037         class Allocator2>
0038       friend class concurrent_node_map;
0039       template <class Key2, class T2, class Hash2, class Pred2,
0040         class Allocator2>
0041       friend class unordered_node_map;
0042 
0043       using type_policy = detail::foa::node_map_types<Key, T,
0044         typename boost::allocator_void_pointer<Allocator>::type>;
0045 
0046       using table_type =
0047         detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
0048 
0049       table_type table_;
0050 
0051       template <class K, class V, class H, class KE, class A>
0052       bool friend operator==(concurrent_node_map<K, V, H, KE, A> const& lhs,
0053         concurrent_node_map<K, V, H, KE, A> const& rhs);
0054 
0055       template <class K, class V, class H, class KE, class A, class Predicate>
0056       friend typename concurrent_node_map<K, V, H, KE, A>::size_type erase_if(
0057         concurrent_node_map<K, V, H, KE, A>& set, Predicate pred);
0058 
0059       template<class Archive, class K, class V, class H, class KE, class A>
0060       friend void serialize(
0061         Archive& ar, concurrent_node_map<K, V, H, KE, A>& c,
0062         unsigned int version);
0063 
0064     public:
0065       using key_type = Key;
0066       using mapped_type = T;
0067       using value_type = typename type_policy::value_type;
0068       using init_type = typename type_policy::init_type;
0069       using size_type = std::size_t;
0070       using difference_type = std::ptrdiff_t;
0071       using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
0072       using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
0073       using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
0074       using reference = value_type&;
0075       using const_reference = value_type const&;
0076       using pointer = typename boost::allocator_pointer<allocator_type>::type;
0077       using const_pointer =
0078         typename boost::allocator_const_pointer<allocator_type>::type;
0079       using node_type = detail::foa::node_map_handle<type_policy,
0080         typename boost::allocator_rebind<Allocator,
0081           typename type_policy::value_type>::type>;
0082       using insert_return_type =
0083         detail::foa::iteratorless_insert_return_type<node_type>;
0084       static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
0085 
0086 #if defined(BOOST_UNORDERED_ENABLE_STATS)
0087       using stats = typename table_type::stats;
0088 #endif
0089 
0090       concurrent_node_map()
0091           : concurrent_node_map(detail::foa::default_bucket_count)
0092       {
0093       }
0094 
0095       explicit concurrent_node_map(size_type n, const hasher& hf = hasher(),
0096         const key_equal& eql = key_equal(),
0097         const allocator_type& a = allocator_type())
0098           : table_(n, hf, eql, a)
0099       {
0100       }
0101 
0102       template <class InputIterator>
0103       concurrent_node_map(InputIterator f, InputIterator l,
0104         size_type n = detail::foa::default_bucket_count,
0105         const hasher& hf = hasher(), const key_equal& eql = key_equal(),
0106         const allocator_type& a = allocator_type())
0107           : table_(n, hf, eql, a)
0108       {
0109         this->insert(f, l);
0110       }
0111 
0112       concurrent_node_map(concurrent_node_map const& rhs)
0113           : table_(rhs.table_,
0114               boost::allocator_select_on_container_copy_construction(
0115                 rhs.get_allocator()))
0116       {
0117       }
0118 
0119       concurrent_node_map(concurrent_node_map&& rhs)
0120           : table_(std::move(rhs.table_))
0121       {
0122       }
0123 
0124       template <class InputIterator>
0125       concurrent_node_map(
0126         InputIterator f, InputIterator l, allocator_type const& a)
0127           : concurrent_node_map(f, l, 0, hasher(), key_equal(), a)
0128       {
0129       }
0130 
0131       explicit concurrent_node_map(allocator_type const& a)
0132           : table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
0133       {
0134       }
0135 
0136       concurrent_node_map(
0137         concurrent_node_map const& rhs, allocator_type const& a)
0138           : table_(rhs.table_, a)
0139       {
0140       }
0141 
0142       concurrent_node_map(concurrent_node_map&& rhs, allocator_type const& a)
0143           : table_(std::move(rhs.table_), a)
0144       {
0145       }
0146 
0147       concurrent_node_map(std::initializer_list<value_type> il,
0148         size_type n = detail::foa::default_bucket_count,
0149         const hasher& hf = hasher(), const key_equal& eql = key_equal(),
0150         const allocator_type& a = allocator_type())
0151           : concurrent_node_map(n, hf, eql, a)
0152       {
0153         this->insert(il.begin(), il.end());
0154       }
0155 
0156       concurrent_node_map(size_type n, const allocator_type& a)
0157           : concurrent_node_map(n, hasher(), key_equal(), a)
0158       {
0159       }
0160 
0161       concurrent_node_map(
0162         size_type n, const hasher& hf, const allocator_type& a)
0163           : concurrent_node_map(n, hf, key_equal(), a)
0164       {
0165       }
0166 
0167       template <typename InputIterator>
0168       concurrent_node_map(
0169         InputIterator f, InputIterator l, size_type n, const allocator_type& a)
0170           : concurrent_node_map(f, l, n, hasher(), key_equal(), a)
0171       {
0172       }
0173 
0174       template <typename InputIterator>
0175       concurrent_node_map(InputIterator f, InputIterator l, size_type n,
0176         const hasher& hf, const allocator_type& a)
0177           : concurrent_node_map(f, l, n, hf, key_equal(), a)
0178       {
0179       }
0180 
0181       concurrent_node_map(
0182         std::initializer_list<value_type> il, const allocator_type& a)
0183           : concurrent_node_map(
0184               il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
0185       {
0186       }
0187 
0188       concurrent_node_map(std::initializer_list<value_type> il, size_type n,
0189         const allocator_type& a)
0190           : concurrent_node_map(il, n, hasher(), key_equal(), a)
0191       {
0192       }
0193 
0194       concurrent_node_map(std::initializer_list<value_type> il, size_type n,
0195         const hasher& hf, const allocator_type& a)
0196           : concurrent_node_map(il, n, hf, key_equal(), a)
0197       {
0198       }
0199 
0200       template <bool avoid_explicit_instantiation = true>
0201       concurrent_node_map(
0202         unordered_node_map<Key, T, Hash, Pred, Allocator>&& other)
0203           : table_(std::move(other.table_))
0204       {
0205       }
0206 
0207       ~concurrent_node_map() = default;
0208 
0209       concurrent_node_map& operator=(concurrent_node_map const& rhs)
0210       {
0211         table_ = rhs.table_;
0212         return *this;
0213       }
0214 
0215       concurrent_node_map& operator=(concurrent_node_map&& rhs) noexcept(
0216         noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
0217       {
0218         table_ = std::move(rhs.table_);
0219         return *this;
0220       }
0221 
0222       concurrent_node_map& operator=(std::initializer_list<value_type> ilist)
0223       {
0224         table_ = ilist;
0225         return *this;
0226       }
0227 
0228       /// Capacity
0229       ///
0230 
0231       size_type size() const noexcept { return table_.size(); }
0232       size_type max_size() const noexcept { return table_.max_size(); }
0233 
0234       BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
0235       {
0236         return size() == 0;
0237       }
0238 
0239       template <class F>
0240       BOOST_FORCEINLINE size_type visit(key_type const& k, F f)
0241       {
0242         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0243         return table_.visit(k, f);
0244       }
0245 
0246       template <class F>
0247       BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
0248       {
0249         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0250         return table_.visit(k, f);
0251       }
0252 
0253       template <class F>
0254       BOOST_FORCEINLINE size_type cvisit(key_type const& k, F f) const
0255       {
0256         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0257         return table_.visit(k, f);
0258       }
0259 
0260       template <class K, class F>
0261       BOOST_FORCEINLINE typename std::enable_if<
0262         detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
0263       visit(K&& k, F f)
0264       {
0265         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0266         return table_.visit(std::forward<K>(k), f);
0267       }
0268 
0269       template <class K, class F>
0270       BOOST_FORCEINLINE typename std::enable_if<
0271         detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
0272       visit(K&& k, F f) const
0273       {
0274         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0275         return table_.visit(std::forward<K>(k), f);
0276       }
0277 
0278       template <class K, class F>
0279       BOOST_FORCEINLINE typename std::enable_if<
0280         detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
0281       cvisit(K&& k, F f) const
0282       {
0283         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0284         return table_.visit(std::forward<K>(k), f);
0285       }
0286 
0287       template<class FwdIterator, class F>
0288       BOOST_FORCEINLINE
0289       size_t visit(FwdIterator first, FwdIterator last, F f)
0290       {
0291         BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
0292         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0293         return table_.visit(first, last, f);
0294       }
0295 
0296       template<class FwdIterator, class F>
0297       BOOST_FORCEINLINE
0298       size_t visit(FwdIterator first, FwdIterator last, F f) const
0299       {
0300         BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
0301         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0302         return table_.visit(first, last, f);
0303       }
0304 
0305       template<class FwdIterator, class F>
0306       BOOST_FORCEINLINE
0307       size_t cvisit(FwdIterator first, FwdIterator last, F f) const
0308       {
0309         BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
0310         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0311         return table_.visit(first, last, f);
0312       }
0313 
0314       template <class F> size_type visit_all(F f)
0315       {
0316         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0317         return table_.visit_all(f);
0318       }
0319 
0320       template <class F> size_type visit_all(F f) const
0321       {
0322         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0323         return table_.visit_all(f);
0324       }
0325 
0326       template <class F> size_type cvisit_all(F f) const
0327       {
0328         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0329         return table_.cvisit_all(f);
0330       }
0331 
0332 #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
0333       template <class ExecPolicy, class F>
0334       typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
0335         void>::type
0336       visit_all(ExecPolicy&& p, F f)
0337       {
0338         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0339         BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
0340         table_.visit_all(p, f);
0341       }
0342 
0343       template <class ExecPolicy, class F>
0344       typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
0345         void>::type
0346       visit_all(ExecPolicy&& p, F f) const
0347       {
0348         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0349         BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
0350         table_.visit_all(p, f);
0351       }
0352 
0353       template <class ExecPolicy, class F>
0354       typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
0355         void>::type
0356       cvisit_all(ExecPolicy&& p, F f) const
0357       {
0358         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0359         BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
0360         table_.cvisit_all(p, f);
0361       }
0362 #endif
0363 
0364       template <class F> bool visit_while(F f)
0365       {
0366         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0367         return table_.visit_while(f);
0368       }
0369 
0370       template <class F> bool visit_while(F f) const
0371       {
0372         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0373         return table_.visit_while(f);
0374       }
0375 
0376       template <class F> bool cvisit_while(F f) const
0377       {
0378         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0379         return table_.cvisit_while(f);
0380       }
0381 
0382 #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
0383       template <class ExecPolicy, class F>
0384       typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
0385         bool>::type
0386       visit_while(ExecPolicy&& p, F f)
0387       {
0388         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0389         BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
0390         return table_.visit_while(p, f);
0391       }
0392 
0393       template <class ExecPolicy, class F>
0394       typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
0395         bool>::type
0396       visit_while(ExecPolicy&& p, F f) const
0397       {
0398         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0399         BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
0400         return table_.visit_while(p, f);
0401       }
0402 
0403       template <class ExecPolicy, class F>
0404       typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
0405         bool>::type
0406       cvisit_while(ExecPolicy&& p, F f) const
0407       {
0408         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0409         BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
0410         return table_.cvisit_while(p, f);
0411       }
0412 #endif
0413 
0414       /// Modifiers
0415       ///
0416 
0417       template <class Ty>
0418       BOOST_FORCEINLINE auto insert(Ty&& value)
0419         -> decltype(table_.insert(std::forward<Ty>(value)))
0420       {
0421         return table_.insert(std::forward<Ty>(value));
0422       }
0423 
0424       BOOST_FORCEINLINE bool insert(init_type&& obj)
0425       {
0426         return table_.insert(std::move(obj));
0427       }
0428 
0429       template <class InputIterator>
0430       size_type insert(InputIterator begin, InputIterator end)
0431       {
0432         size_type count_elements = 0;
0433         for (auto pos = begin; pos != end; ++pos, ++count_elements) {
0434           table_.emplace(*pos);
0435         }
0436         return count_elements;
0437       }
0438 
0439       size_type insert(std::initializer_list<value_type> ilist)
0440       {
0441         return this->insert(ilist.begin(), ilist.end());
0442       }
0443 
0444       insert_return_type insert(node_type&& nh)
0445       {
0446         using access = detail::foa::node_handle_access;
0447 
0448         if (nh.empty()) {
0449           return {false, node_type{}};
0450         }
0451 
0452         // Caveat: get_allocator() incurs synchronization (not cheap)
0453         BOOST_ASSERT(get_allocator() == nh.get_allocator());
0454 
0455         if (table_.insert(std::move(access::element(nh)))) {
0456           access::reset(nh);
0457           return {true, node_type{}};
0458         } else {
0459           return {false, std::move(nh)};
0460         }
0461       }
0462 
0463       template <class M>
0464       BOOST_FORCEINLINE bool insert_or_assign(key_type const& k, M&& obj)
0465       {
0466         return table_.try_emplace_or_visit(k, std::forward<M>(obj),
0467           [&](value_type& m) { m.second = std::forward<M>(obj); });
0468       }
0469 
0470       template <class M>
0471       BOOST_FORCEINLINE bool insert_or_assign(key_type&& k, M&& obj)
0472       {
0473         return table_.try_emplace_or_visit(std::move(k), std::forward<M>(obj),
0474           [&](value_type& m) { m.second = std::forward<M>(obj); });
0475       }
0476 
0477       template <class K, class M>
0478       BOOST_FORCEINLINE typename std::enable_if<
0479         detail::are_transparent<K, hasher, key_equal>::value, bool>::type
0480       insert_or_assign(K&& k, M&& obj)
0481       {
0482         return table_.try_emplace_or_visit(std::forward<K>(k),
0483           std::forward<M>(obj),
0484           [&](value_type& m) { m.second = std::forward<M>(obj); });
0485       }
0486 
0487       template <class Ty, class F>
0488       BOOST_FORCEINLINE auto insert_or_visit(Ty&& value, F f)
0489         -> decltype(table_.insert_or_visit(std::forward<Ty>(value), f))
0490       {
0491         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0492         return table_.insert_or_visit(std::forward<Ty>(value), f);
0493       }
0494 
0495       template <class F>
0496       BOOST_FORCEINLINE bool insert_or_visit(init_type&& obj, F f)
0497       {
0498         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0499         return table_.insert_or_visit(std::move(obj), f);
0500       }
0501 
0502       template <class InputIterator, class F>
0503       size_type insert_or_visit(InputIterator first, InputIterator last, F f)
0504       {
0505         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0506         size_type count_elements = 0;
0507         for (; first != last; ++first, ++count_elements) {
0508           table_.emplace_or_visit(*first, f);
0509         }
0510         return count_elements;
0511       }
0512 
0513       template <class F>
0514       size_type insert_or_visit(std::initializer_list<value_type> ilist, F f)
0515       {
0516         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0517         return this->insert_or_visit(ilist.begin(), ilist.end(), std::ref(f));
0518       }
0519 
0520       template <class F>
0521       insert_return_type insert_or_visit(node_type&& nh, F f)
0522       {
0523         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
0524         using access = detail::foa::node_handle_access;
0525 
0526         if (nh.empty()) {
0527           return {false, node_type{}};
0528         }
0529 
0530         // Caveat: get_allocator() incurs synchronization (not cheap)
0531         BOOST_ASSERT(get_allocator() == nh.get_allocator());
0532 
0533         if (table_.insert_or_visit(std::move(access::element(nh)), f)) {
0534           access::reset(nh);
0535           return {true, node_type{}};
0536         } else {
0537           return {false, std::move(nh)};
0538         }
0539       }
0540 
0541       template <class Ty, class F>
0542       BOOST_FORCEINLINE auto insert_or_cvisit(Ty&& value, F f)
0543         -> decltype(table_.insert_or_cvisit(std::forward<Ty>(value), f))
0544       {
0545         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0546         return table_.insert_or_cvisit(std::forward<Ty>(value), f);
0547       }
0548 
0549       template <class F>
0550       BOOST_FORCEINLINE bool insert_or_cvisit(init_type&& obj, F f)
0551       {
0552         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0553         return table_.insert_or_cvisit(std::move(obj), f);
0554       }
0555 
0556       template <class InputIterator, class F>
0557       size_type insert_or_cvisit(InputIterator first, InputIterator last, F f)
0558       {
0559         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0560         size_type count_elements = 0;
0561         for (; first != last; ++first, ++count_elements) {
0562           table_.emplace_or_cvisit(*first, f);
0563         }
0564         return count_elements;
0565       }
0566 
0567       template <class F>
0568       size_type insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
0569       {
0570         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0571         return this->insert_or_cvisit(ilist.begin(), ilist.end(), std::ref(f));
0572       }
0573 
0574       template <class F>
0575       insert_return_type insert_or_cvisit(node_type&& nh, F f)
0576       {
0577         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
0578         using access = detail::foa::node_handle_access;
0579 
0580         if (nh.empty()) {
0581           return {false, node_type{}};
0582         }
0583 
0584         // Caveat: get_allocator() incurs synchronization (not cheap)
0585         BOOST_ASSERT(get_allocator() == nh.get_allocator());
0586 
0587         if (table_.insert_or_cvisit(std::move(access::element(nh)), f)) {
0588           access::reset(nh);
0589           return {true, node_type{}};
0590         } else {
0591           return {false, std::move(nh)};
0592         }
0593       }
0594 
0595       template <class Ty, class F1, class F2>
0596       BOOST_FORCEINLINE auto insert_and_visit(Ty&& value, F1 f1, F2 f2)
0597         -> decltype(table_.insert_and_visit(std::forward<Ty>(value), f1, f2))
0598       {
0599         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
0600         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
0601         return table_.insert_and_visit(std::forward<Ty>(value), f1, f2);
0602       }
0603 
0604       template <class F1, class F2>
0605       BOOST_FORCEINLINE bool insert_and_visit(init_type&& obj, F1 f1, F2 f2)
0606       {
0607         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
0608         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
0609         return table_.insert_and_visit(std::move(obj), f1, f2);
0610       }
0611 
0612       template <class InputIterator, class F1, class F2>
0613       size_type insert_and_visit(
0614         InputIterator first, InputIterator last, F1 f1, F2 f2)
0615       {
0616         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
0617         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
0618         size_type count_elements = 0;
0619         for (; first != last; ++first, ++count_elements) {
0620           table_.emplace_and_visit(*first, f1, f2);
0621         }
0622         return count_elements;
0623       }
0624 
0625       template <class F1, class F2>
0626       size_type insert_and_visit(
0627         std::initializer_list<value_type> ilist, F1 f1, F2 f2)
0628       {
0629         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
0630         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
0631         return this->insert_and_visit(
0632           ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
0633       }
0634 
0635       template <class F1, class F2>
0636       insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2)
0637       {
0638         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
0639         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
0640         using access = detail::foa::node_handle_access;
0641 
0642         if (nh.empty()) {
0643           return {false, node_type{}};
0644         }
0645 
0646         // Caveat: get_allocator() incurs synchronization (not cheap)
0647         BOOST_ASSERT(get_allocator() == nh.get_allocator());
0648 
0649         if (table_.insert_and_visit(std::move(access::element(nh)), f1, f2)) {
0650           access::reset(nh);
0651           return {true, node_type{}};
0652         } else {
0653           return {false, std::move(nh)};
0654         }
0655       }
0656 
0657       template <class Ty, class F1, class F2>
0658       BOOST_FORCEINLINE auto insert_and_cvisit(Ty&& value, F1 f1, F2 f2)
0659         -> decltype(table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2))
0660       {
0661         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
0662         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
0663         return table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2);
0664       }
0665 
0666       template <class F1, class F2>
0667       BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2)
0668       {
0669         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
0670         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
0671         return table_.insert_and_cvisit(std::move(obj), f1, f2);
0672       }
0673 
0674       template <class InputIterator, class F1, class F2>
0675       size_type insert_and_cvisit(
0676         InputIterator first, InputIterator last, F1 f1, F2 f2)
0677       {
0678         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
0679         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
0680         size_type count_elements = 0;
0681         for (; first != last; ++first, ++count_elements) {
0682           table_.emplace_and_cvisit(*first, f1, f2);
0683         }
0684         return count_elements;
0685       }
0686 
0687       template <class F1, class F2>
0688       size_type insert_and_cvisit(
0689         std::initializer_list<value_type> ilist, F1 f1, F2 f2)
0690       {
0691         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
0692         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
0693         return this->insert_and_cvisit(
0694           ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
0695       }
0696 
0697       template <class F1, class F2>
0698       insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2)
0699       {
0700         BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
0701         BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
0702         using access = detail::foa::node_handle_access;
0703 
0704         if (nh.empty()) {
0705           return {false, node_type{}};
0706         }
0707 
0708         // Caveat: get_allocator() incurs synchronization (not cheap)
0709         BOOST_ASSERT(get_allocator() == nh.get_allocator());
0710 
0711         if (table_.insert_and_cvisit(std::move(access::element(nh)), f1, f2)) {
0712           access::reset(nh);
0713           return {true, node_type{}};
0714         } else {
0715           return {false, std::move(nh)};
0716         }
0717       }
0718 
0719       template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
0720       {
0721         return table_.emplace(std::forward<Args>(args)...);
0722       }
0723 
0724       template <class Arg, class... Args>
0725       BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
0726       {
0727         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
0728         return table_.emplace_or_visit(
0729           std::forward<Arg>(arg), std::forward<Args>(args)...);
0730       }
0731 
0732       template <class Arg, class... Args>
0733       BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args)
0734       {
0735         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
0736         return table_.emplace_or_cvisit(
0737           std::forward<Arg>(arg), std::forward<Args>(args)...);
0738       }
0739 
0740       template <class Arg1, class Arg2, class... Args>
0741       BOOST_FORCEINLINE bool emplace_and_visit(
0742          Arg1&& arg1, Arg2&& arg2, Args&&... args)
0743       {
0744         BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
0745           Arg1, Arg2, Args...)
0746         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
0747         return table_.emplace_and_visit(
0748           std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
0749           std::forward<Args>(args)...);
0750       }
0751 
0752       template <class Arg1, class Arg2, class... Args>
0753       BOOST_FORCEINLINE bool emplace_and_cvisit(
0754         Arg1&& arg1, Arg2&& arg2, Args&&... args)
0755       {
0756         BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
0757           Arg1, Arg2, Args...)
0758         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
0759         return table_.emplace_and_cvisit(
0760           std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
0761           std::forward<Args>(args)...);
0762       }
0763 
0764       template <class... Args>
0765       BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
0766       {
0767         return table_.try_emplace(k, std::forward<Args>(args)...);
0768       }
0769 
0770       template <class... Args>
0771       BOOST_FORCEINLINE bool try_emplace(key_type&& k, Args&&... args)
0772       {
0773         return table_.try_emplace(std::move(k), std::forward<Args>(args)...);
0774       }
0775 
0776       template <class K, class... Args>
0777       BOOST_FORCEINLINE typename std::enable_if<
0778         detail::are_transparent<K, hasher, key_equal>::value, bool>::type
0779       try_emplace(K&& k, Args&&... args)
0780       {
0781         return table_.try_emplace(
0782           std::forward<K>(k), std::forward<Args>(args)...);
0783       }
0784 
0785       template <class Arg, class... Args>
0786       BOOST_FORCEINLINE bool try_emplace_or_visit(
0787         key_type const& k, Arg&& arg, Args&&... args)
0788       {
0789         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
0790         return table_.try_emplace_or_visit(
0791           k, std::forward<Arg>(arg), std::forward<Args>(args)...);
0792       }
0793 
0794       template <class Arg, class... Args>
0795       BOOST_FORCEINLINE bool try_emplace_or_cvisit(
0796         key_type const& k, Arg&& arg, Args&&... args)
0797       {
0798         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
0799         return table_.try_emplace_or_cvisit(
0800           k, std::forward<Arg>(arg), std::forward<Args>(args)...);
0801       }
0802 
0803       template <class Arg, class... Args>
0804       BOOST_FORCEINLINE bool try_emplace_or_visit(
0805         key_type&& k, Arg&& arg, Args&&... args)
0806       {
0807         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
0808         return table_.try_emplace_or_visit(
0809           std::move(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
0810       }
0811 
0812       template <class Arg, class... Args>
0813       BOOST_FORCEINLINE bool try_emplace_or_cvisit(
0814         key_type&& k, Arg&& arg, Args&&... args)
0815       {
0816         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
0817         return table_.try_emplace_or_cvisit(
0818           std::move(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
0819       }
0820 
0821       template <class K, class Arg, class... Args>
0822       BOOST_FORCEINLINE bool try_emplace_or_visit(
0823         K&& k, Arg&& arg, Args&&... args)
0824       {
0825         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
0826         return table_.try_emplace_or_visit(std::forward<K>(k),
0827           std::forward<Arg>(arg), std::forward<Args>(args)...);
0828       }
0829 
0830       template <class K, class Arg, class... Args>
0831       BOOST_FORCEINLINE bool try_emplace_or_cvisit(
0832         K&& k, Arg&& arg, Args&&... args)
0833       {
0834         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
0835         return table_.try_emplace_or_cvisit(std::forward<K>(k),
0836           std::forward<Arg>(arg), std::forward<Args>(args)...);
0837       }
0838 
0839       template <class Arg1, class Arg2, class... Args>
0840       BOOST_FORCEINLINE bool try_emplace_and_visit(
0841         key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
0842       {
0843         BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
0844           Arg1, Arg2, Args...)
0845         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
0846         return table_.try_emplace_and_visit(
0847           k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
0848           std::forward<Args>(args)...);
0849       }
0850 
0851       template <class Arg1, class Arg2, class... Args>
0852       BOOST_FORCEINLINE bool try_emplace_and_cvisit(
0853         key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
0854       {
0855         BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
0856           Arg1, Arg2, Args...)
0857         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
0858         return table_.try_emplace_and_cvisit(
0859           k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
0860           std::forward<Args>(args)...);
0861       }
0862 
0863       template <class Arg1, class Arg2, class... Args>
0864       BOOST_FORCEINLINE bool try_emplace_and_visit(
0865         key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
0866       {
0867         BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
0868           Arg1, Arg2, Args...)
0869         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
0870         return table_.try_emplace_and_visit(
0871           std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
0872           std::forward<Args>(args)...);
0873       }
0874 
0875       template <class Arg1, class Arg2, class... Args>
0876       BOOST_FORCEINLINE bool try_emplace_and_cvisit(
0877         key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
0878       {
0879         BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
0880           Arg1, Arg2, Args...)
0881         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
0882         return table_.try_emplace_and_cvisit(
0883           std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
0884           std::forward<Args>(args)...);
0885       }
0886 
0887       template <class K, class Arg1, class Arg2, class... Args>
0888       BOOST_FORCEINLINE bool try_emplace_and_visit(
0889         K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
0890       {
0891         BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
0892           Arg1, Arg2, Args...)
0893         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
0894         return table_.try_emplace_and_visit(std::forward<K>(k),
0895           std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
0896           std::forward<Args>(args)...);
0897       }
0898 
0899       template <class K, class Arg1, class Arg2, class... Args>
0900       BOOST_FORCEINLINE bool try_emplace_and_cvisit(
0901         K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
0902       {
0903         BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
0904           Arg1, Arg2, Args...)
0905         BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
0906         return table_.try_emplace_and_cvisit(std::forward<K>(k),
0907           std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
0908           std::forward<Args>(args)...);
0909       }
0910 
0911       BOOST_FORCEINLINE size_type erase(key_type const& k)
0912       {
0913         return table_.erase(k);
0914       }
0915 
0916       template <class K>
0917       BOOST_FORCEINLINE typename std::enable_if<
0918         detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
0919       erase(K&& k)
0920       {
0921         return table_.erase(std::forward<K>(k));
0922       }
0923 
0924       template <class F>
0925       BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
0926       {
0927         return table_.erase_if(k, f);
0928       }
0929 
0930       template <class K, class F>
0931       BOOST_FORCEINLINE typename std::enable_if<
0932         detail::are_transparent<K, hasher, key_equal>::value &&
0933           !detail::is_execution_policy<K>::value,
0934         size_type>::type
0935       erase_if(K&& k, F f)
0936       {
0937         return table_.erase_if(std::forward<K>(k), f);
0938       }
0939 
0940 #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
0941       template <class ExecPolicy, class F>
0942       typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
0943         void>::type
0944       erase_if(ExecPolicy&& p, F f)
0945       {
0946         BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
0947         table_.erase_if(p, f);
0948       }
0949 #endif
0950 
0951       template <class F> size_type erase_if(F f) { return table_.erase_if(f); }
0952 
0953       void swap(concurrent_node_map& other) noexcept(
0954         boost::allocator_is_always_equal<Allocator>::type::value ||
0955         boost::allocator_propagate_on_container_swap<Allocator>::type::value)
0956       {
0957         return table_.swap(other.table_);
0958       }
0959 
0960       node_type extract(key_type const& key)
0961       {
0962         node_type nh;
0963         table_.extract(key, detail::foa::node_handle_emplacer(nh));
0964         return nh;
0965       }
0966 
0967       template <class K>
0968       typename std::enable_if<
0969         detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
0970       extract(K const& key)
0971       {
0972         node_type nh;
0973         table_.extract(key, detail::foa::node_handle_emplacer(nh));
0974         return nh;
0975       }
0976 
0977       template <class F>
0978       node_type extract_if(key_type const& key, F f)
0979       {
0980         node_type nh;
0981         table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
0982         return nh;
0983       }
0984 
0985       template <class K, class F>
0986       typename std::enable_if<
0987         detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
0988       extract_if(K const& key, F f)
0989       {
0990         node_type nh;
0991         table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
0992         return nh;
0993       }
0994 
0995       void clear() noexcept { table_.clear(); }
0996 
0997       template <typename H2, typename P2>
0998       size_type merge(concurrent_node_map<Key, T, H2, P2, Allocator>& x)
0999       {
1000         BOOST_ASSERT(get_allocator() == x.get_allocator());
1001         return table_.merge(x.table_);
1002       }
1003 
1004       template <typename H2, typename P2>
1005       size_type merge(concurrent_node_map<Key, T, H2, P2, Allocator>&& x)
1006       {
1007         return merge(x);
1008       }
1009 
1010       BOOST_FORCEINLINE size_type count(key_type const& k) const
1011       {
1012         return table_.count(k);
1013       }
1014 
1015       template <class K>
1016       BOOST_FORCEINLINE typename std::enable_if<
1017         detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
1018       count(K const& k)
1019       {
1020         return table_.count(k);
1021       }
1022 
1023       BOOST_FORCEINLINE bool contains(key_type const& k) const
1024       {
1025         return table_.contains(k);
1026       }
1027 
1028       template <class K>
1029       BOOST_FORCEINLINE typename std::enable_if<
1030         detail::are_transparent<K, hasher, key_equal>::value, bool>::type
1031       contains(K const& k) const
1032       {
1033         return table_.contains(k);
1034       }
1035 
1036       /// Hash Policy
1037       ///
1038       size_type bucket_count() const noexcept { return table_.capacity(); }
1039 
1040       float load_factor() const noexcept { return table_.load_factor(); }
1041       float max_load_factor() const noexcept
1042       {
1043         return table_.max_load_factor();
1044       }
1045       void max_load_factor(float) {}
1046       size_type max_load() const noexcept { return table_.max_load(); }
1047 
1048       void rehash(size_type n) { table_.rehash(n); }
1049       void reserve(size_type n) { table_.reserve(n); }
1050 
1051 #if defined(BOOST_UNORDERED_ENABLE_STATS)
1052       /// Stats
1053       ///
1054       stats get_stats() const { return table_.get_stats(); }
1055 
1056       void reset_stats() noexcept { table_.reset_stats(); }
1057 #endif
1058 
1059       /// Observers
1060       ///
1061       allocator_type get_allocator() const noexcept
1062       {
1063         return table_.get_allocator();
1064       }
1065 
1066       hasher hash_function() const { return table_.hash_function(); }
1067       key_equal key_eq() const { return table_.key_eq(); }
1068     };
1069 
1070     template <class Key, class T, class Hash, class KeyEqual, class Allocator>
1071     bool operator==(
1072       concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
1073       concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
1074     {
1075       return lhs.table_ == rhs.table_;
1076     }
1077 
1078     template <class Key, class T, class Hash, class KeyEqual, class Allocator>
1079     bool operator!=(
1080       concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
1081       concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
1082     {
1083       return !(lhs == rhs);
1084     }
1085 
1086     template <class Key, class T, class Hash, class Pred, class Alloc>
1087     void swap(concurrent_node_map<Key, T, Hash, Pred, Alloc>& x,
1088       concurrent_node_map<Key, T, Hash, Pred, Alloc>& y)
1089       noexcept(noexcept(x.swap(y)))
1090     {
1091       x.swap(y);
1092     }
1093 
1094     template <class K, class T, class H, class P, class A, class Predicate>
1095     typename concurrent_node_map<K, T, H, P, A>::size_type erase_if(
1096       concurrent_node_map<K, T, H, P, A>& c, Predicate pred)
1097     {
1098       return c.table_.erase_if(pred);
1099     }
1100 
1101     template<class Archive, class K, class V, class H, class KE, class A>
1102     void serialize(
1103       Archive& ar, concurrent_node_map<K, V, H, KE, A>& c, unsigned int)
1104     {
1105       ar & core::make_nvp("table",c.table_);
1106     }
1107 
1108 #if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
1109 
1110     template <class InputIterator,
1111       class Hash =
1112         boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
1113       class Pred =
1114         std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
1115       class Allocator = std::allocator<
1116         boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
1117       class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
1118       class = std::enable_if_t<detail::is_hash_v<Hash> >,
1119       class = std::enable_if_t<detail::is_pred_v<Pred> >,
1120       class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
1121     concurrent_node_map(InputIterator, InputIterator,
1122       std::size_t = boost::unordered::detail::foa::default_bucket_count,
1123       Hash = Hash(), Pred = Pred(), Allocator = Allocator())
1124       -> concurrent_node_map<
1125         boost::unordered::detail::iter_key_t<InputIterator>,
1126         boost::unordered::detail::iter_val_t<InputIterator>, Hash, Pred,
1127         Allocator>;
1128 
1129     template <class Key, class T,
1130       class Hash = boost::hash<std::remove_const_t<Key> >,
1131       class Pred = std::equal_to<std::remove_const_t<Key> >,
1132       class Allocator = std::allocator<std::pair<const Key, T> >,
1133       class = std::enable_if_t<detail::is_hash_v<Hash> >,
1134       class = std::enable_if_t<detail::is_pred_v<Pred> >,
1135       class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
1136     concurrent_node_map(std::initializer_list<std::pair<Key, T> >,
1137       std::size_t = boost::unordered::detail::foa::default_bucket_count,
1138       Hash = Hash(), Pred = Pred(), Allocator = Allocator())
1139       -> concurrent_node_map<std::remove_const_t<Key>, T, Hash, Pred,
1140         Allocator>;
1141 
1142     template <class InputIterator, class Allocator,
1143       class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
1144       class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
1145     concurrent_node_map(InputIterator, InputIterator, std::size_t, Allocator)
1146       -> concurrent_node_map<
1147         boost::unordered::detail::iter_key_t<InputIterator>,
1148         boost::unordered::detail::iter_val_t<InputIterator>,
1149         boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
1150         std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
1151         Allocator>;
1152 
1153     template <class InputIterator, class Allocator,
1154       class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
1155       class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
1156     concurrent_node_map(InputIterator, InputIterator, Allocator)
1157       -> concurrent_node_map<
1158         boost::unordered::detail::iter_key_t<InputIterator>,
1159         boost::unordered::detail::iter_val_t<InputIterator>,
1160         boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
1161         std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
1162         Allocator>;
1163 
1164     template <class InputIterator, class Hash, class Allocator,
1165       class = std::enable_if_t<detail::is_hash_v<Hash> >,
1166       class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
1167       class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
1168     concurrent_node_map(
1169       InputIterator, InputIterator, std::size_t, Hash, Allocator)
1170       -> concurrent_node_map<
1171         boost::unordered::detail::iter_key_t<InputIterator>,
1172         boost::unordered::detail::iter_val_t<InputIterator>, Hash,
1173         std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
1174         Allocator>;
1175 
1176     template <class Key, class T, class Allocator,
1177       class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
1178     concurrent_node_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
1179       Allocator) -> concurrent_node_map<std::remove_const_t<Key>, T,
1180       boost::hash<std::remove_const_t<Key> >,
1181       std::equal_to<std::remove_const_t<Key> >, Allocator>;
1182 
1183     template <class Key, class T, class Allocator,
1184       class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
1185     concurrent_node_map(std::initializer_list<std::pair<Key, T> >, Allocator)
1186       -> concurrent_node_map<std::remove_const_t<Key>, T,
1187         boost::hash<std::remove_const_t<Key> >,
1188         std::equal_to<std::remove_const_t<Key> >, Allocator>;
1189 
1190     template <class Key, class T, class Hash, class Allocator,
1191       class = std::enable_if_t<detail::is_hash_v<Hash> >,
1192       class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
1193     concurrent_node_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
1194       Hash, Allocator) -> concurrent_node_map<std::remove_const_t<Key>, T,
1195       Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
1196 
1197 #endif
1198 
1199   } // namespace unordered
1200 } // namespace boost
1201 
1202 #endif // BOOST_UNORDERED_CONCURRENT_NODE_MAP_HPP