Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-01 08:33:08

0001 /* Fast open-addressing hash table.
0002  *
0003  * Copyright 2022-2024 Joaquin M Lopez Munoz.
0004  * Copyright 2023 Christian Mazakas.
0005  * Copyright 2024 Braden Ganetsky.
0006  * Distributed under the Boost Software License, Version 1.0.
0007  * (See accompanying file LICENSE_1_0.txt or copy at
0008  * http://www.boost.org/LICENSE_1_0.txt)
0009  *
0010  * See https://www.boost.org/libs/unordered for library home page.
0011  */
0012 
0013 #ifndef BOOST_UNORDERED_DETAIL_FOA_TABLE_HPP
0014 #define BOOST_UNORDERED_DETAIL_FOA_TABLE_HPP
0015 
0016 #include <boost/assert.hpp>
0017 #include <boost/config.hpp>
0018 #include <boost/config/workaround.hpp>
0019 #include <boost/core/serialization.hpp>
0020 #include <boost/unordered/detail/foa/core.hpp>
0021 #include <boost/unordered/detail/serialize_tracked_address.hpp>
0022 #include <boost/unordered/detail/type_traits.hpp>
0023 #include <cstddef>
0024 #include <iterator>
0025 #include <memory>
0026 #include <type_traits>
0027 #include <utility>
0028 
0029 namespace boost{
0030 namespace unordered{
0031 namespace detail{
0032 namespace foa{
0033 
0034 /* use plain integrals for group metadata storage */
0035 
0036 template<typename Integral>
0037 struct plain_integral
0038 {
0039   operator Integral()const{return n;}
0040   void operator=(Integral m){n=m;}
0041 
0042 #if BOOST_WORKAROUND(BOOST_GCC,>=50000 && BOOST_GCC<60000)
0043   void operator|=(Integral m){n=static_cast<Integral>(n|m);}
0044   void operator&=(Integral m){n=static_cast<Integral>(n&m);}
0045 #else
0046   void operator|=(Integral m){n|=m;}
0047   void operator&=(Integral m){n&=m;}
0048 #endif
0049 
0050   Integral n;
0051 };
0052 
0053 struct plain_size_control
0054 {
0055   std::size_t ml;
0056   std::size_t size;
0057 };
0058 
0059 template<typename,typename,typename,typename>
0060 class table;
0061 
0062 /* table_iterator keeps two pointers:
0063  * 
0064  *   - A pointer p to the element slot.
0065  *   - A pointer pc to the n-th byte of the associated group metadata, where n
0066  *     is the position of the element in the group.
0067  *
0068  * A simpler solution would have been to keep a pointer p to the element, a
0069  * pointer pg to the group, and the position n, but that would increase
0070  * sizeof(table_iterator) by 4/8 bytes. In order to make this compact
0071  * representation feasible, it is required that group objects are aligned
0072  * to their size, so that we can recover pg and n as
0073  * 
0074  *   - n = pc%sizeof(group)
0075  *   - pg = pc-n
0076  * 
0077  * (for explanatory purposes pg and pc are treated above as if they were memory
0078  * addresses rather than pointers).
0079  * 
0080  * p = nullptr is conventionally used to mark end() iterators.
0081  */
0082 
0083 /* internal conversion from const_iterator to iterator */
0084 struct const_iterator_cast_tag{}; 
0085 
0086 template<typename TypePolicy,typename GroupPtr,bool Const>
0087 class table_iterator
0088 {
0089   using group_pointer_traits=boost::pointer_traits<GroupPtr>;
0090   using type_policy=TypePolicy;
0091   using table_element_type=typename type_policy::element_type;
0092   using group_type=typename group_pointer_traits::element_type;
0093   using table_element_pointer=
0094     typename group_pointer_traits::template rebind<table_element_type>;
0095   using char_pointer=
0096     typename group_pointer_traits::template rebind<unsigned char>;
0097   static constexpr auto N=group_type::N;
0098   static constexpr auto regular_layout=group_type::regular_layout;
0099 
0100 public:
0101   using difference_type=std::ptrdiff_t;
0102   using value_type=typename type_policy::value_type;
0103   using pointer=
0104     typename std::conditional<Const,value_type const*,value_type*>::type;
0105   using reference=
0106     typename std::conditional<Const,value_type const&,value_type&>::type;
0107   using iterator_category=std::forward_iterator_tag;
0108   using element_type=
0109     typename std::conditional<Const,value_type const,value_type>::type;
0110 
0111   table_iterator():pc_{nullptr},p_{nullptr}{};
0112   template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
0113   table_iterator(const table_iterator<TypePolicy,GroupPtr,Const2>& x):
0114     pc_{x.pc_},p_{x.p_}{}
0115   table_iterator(
0116     const_iterator_cast_tag, const table_iterator<TypePolicy,GroupPtr,true>& x):
0117     pc_{x.pc_},p_{x.p_}{}
0118 
0119   inline reference operator*()const noexcept
0120     {return type_policy::value_from(*p());}
0121   inline pointer operator->()const noexcept
0122     {return std::addressof(type_policy::value_from(*p()));}
0123   inline table_iterator& operator++()noexcept{increment();return *this;}
0124   inline table_iterator operator++(int)noexcept
0125     {auto x=*this;increment();return x;}
0126   friend inline bool operator==(
0127     const table_iterator& x,const table_iterator& y)
0128     {return x.p()==y.p();}
0129   friend inline bool operator!=(
0130     const table_iterator& x,const table_iterator& y)
0131     {return !(x==y);}
0132 
0133 private:
0134   template<typename,typename,bool> friend class table_iterator;
0135   template<typename> friend class table_erase_return_type;
0136   template<typename,typename,typename,typename> friend class table;
0137 
0138   table_iterator(group_type* pg,std::size_t n,const table_element_type* ptet):
0139     pc_{to_pointer<char_pointer>(
0140       reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n)},
0141     p_{to_pointer<table_element_pointer>(const_cast<table_element_type*>(ptet))}
0142   {}
0143 
0144   unsigned char* pc()const noexcept{return boost::to_address(pc_);}
0145   table_element_type* p()const noexcept{return boost::to_address(p_);}
0146 
0147   inline void increment()noexcept
0148   {
0149     BOOST_ASSERT(p()!=nullptr);
0150     increment(std::integral_constant<bool,regular_layout>{});
0151   }
0152 
0153   inline void increment(std::true_type /* regular layout */)noexcept
0154   {
0155     using diff_type=
0156       typename boost::pointer_traits<char_pointer>::difference_type;
0157 
0158     for(;;){
0159       ++p_;
0160       if(reinterpret_cast<uintptr_t>(pc())%sizeof(group_type)==N-1){
0161         pc_+=static_cast<diff_type>(sizeof(group_type)-(N-1));
0162         break;
0163       }
0164       ++pc_;
0165       if(!group_type::is_occupied(pc()))continue;
0166       if(BOOST_UNLIKELY(group_type::is_sentinel(pc())))p_=nullptr;
0167       return;
0168     }
0169 
0170     for(;;){
0171       int mask=reinterpret_cast<group_type*>(pc())->match_occupied();
0172       if(mask!=0){
0173         auto n=unchecked_countr_zero(mask);
0174         if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
0175           p_=nullptr;
0176         }
0177         else{
0178           pc_+=static_cast<diff_type>(n);
0179           p_+=static_cast<diff_type>(n);
0180         }
0181         return;
0182       }
0183       pc_+=static_cast<diff_type>(sizeof(group_type));
0184       p_+=static_cast<diff_type>(N);
0185     }
0186   }
0187 
0188   inline void increment(std::false_type /* interleaved */)noexcept
0189   {
0190     using diff_type=
0191       typename boost::pointer_traits<char_pointer>::difference_type;
0192 
0193     std::size_t n0=reinterpret_cast<uintptr_t>(pc())%sizeof(group_type);
0194     pc_-=static_cast<diff_type>(n0);
0195 
0196     int mask=(
0197       reinterpret_cast<group_type*>(pc())->match_occupied()>>(n0+1))<<(n0+1);
0198     if(!mask){
0199       do{
0200         pc_+=sizeof(group_type);
0201         p_+=N;
0202       }
0203       while((mask=reinterpret_cast<group_type*>(pc())->match_occupied())==0);
0204     }
0205 
0206     auto n=unchecked_countr_zero(mask);
0207     if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
0208       p_=nullptr;
0209     }
0210     else{
0211       pc_+=static_cast<diff_type>(n);
0212       p_-=static_cast<diff_type>(n0);
0213       p_+=static_cast<diff_type>(n);
0214     }
0215   }
0216 
0217   template<typename Archive>
0218   friend void serialization_track(Archive& ar,const table_iterator& x)
0219   {
0220     if(x.p()){
0221       track_address(ar,x.pc_);
0222       track_address(ar,x.p_);
0223     }
0224   }
0225 
0226   friend class boost::serialization::access;
0227 
0228   template<typename Archive>
0229   void serialize(Archive& ar,unsigned int)
0230   {
0231     if(!p())pc_=nullptr;
0232     serialize_tracked_address(ar,pc_);
0233     serialize_tracked_address(ar,p_);
0234   }
0235 
0236   char_pointer          pc_=nullptr;
0237   table_element_pointer  p_=nullptr;
0238 };
0239 
0240 /* Returned by table::erase([const_]iterator) to avoid iterator increment
0241  * if discarded.
0242  */
0243 
0244 template<typename Iterator>
0245 class table_erase_return_type; 
0246 
0247 template<typename TypePolicy,typename GroupPtr,bool Const>
0248 class table_erase_return_type<table_iterator<TypePolicy,GroupPtr,Const>>
0249 {
0250   using iterator=table_iterator<TypePolicy,GroupPtr,Const>;
0251   using const_iterator=table_iterator<TypePolicy,GroupPtr,true>;
0252 
0253 public:
0254   /* can't delete it because VS in pre-C++17 mode needs to see it for RVO */
0255   table_erase_return_type(const table_erase_return_type&);
0256 
0257   operator iterator()const noexcept
0258   {
0259     auto it=pos;
0260     it.increment(); /* valid even if *it was erased */
0261     return iterator(const_iterator_cast_tag{},it);
0262   }
0263 
0264   template<
0265     bool dependent_value=false,
0266     typename std::enable_if<!Const||dependent_value>::type* =nullptr
0267   >
0268   operator const_iterator()const noexcept{return this->operator iterator();}
0269 
0270 private:
0271   template<typename,typename,typename,typename> friend class table;
0272 
0273   table_erase_return_type(const_iterator pos_):pos{pos_}{}
0274   table_erase_return_type& operator=(const table_erase_return_type&)=delete;
0275 
0276   const_iterator pos;
0277 };
0278 
0279 /* foa::table interface departs in a number of ways from that of C++ unordered
0280  * associative containers because it's not for end-user consumption
0281  * (boost::unordered_(flat|node)_(map|set) wrappers complete it as
0282  * appropriate).
0283  *
0284  * The table supports two main modes of operation: flat and node-based. In the
0285  * flat case, buckets directly store elements. For node-based, buckets store
0286  * pointers to individually heap-allocated elements.
0287  *
0288  * For both flat and node-based:
0289  *
0290  *   - begin() is not O(1).
0291  *   - No bucket API.
0292  *   - Load factor is fixed and can't be set by the user.
0293  * 
0294  * For flat only:
0295  *
0296  *   - value_type must be moveable.
0297  *   - Pointer stability is not kept under rehashing.
0298  *   - No extract API.
0299  *
0300  * try_emplace, erase and find support heterogeneous lookup by default,
0301  * that is, without checking for any ::is_transparent typedefs --the
0302  * checking is done by boost::unordered_(flat|node)_(map|set).
0303  */
0304 
0305 template<typename,typename,typename,typename>
0306 class concurrent_table; /* concurrent/non-concurrent interop */
0307 
0308 template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
0309 using table_core_impl=
0310   table_core<TypePolicy,group15<plain_integral>,table_arrays,
0311   plain_size_control,Hash,Pred,Allocator>;
0312 
0313 #include <boost/unordered/detail/foa/ignore_wshadow.hpp>
0314 
0315 #if defined(BOOST_MSVC)
0316 #pragma warning(push)
0317 #pragma warning(disable:4714) /* marked as __forceinline not inlined */
0318 #endif
0319 
0320 template<typename TypePolicy,typename Hash,typename Pred,typename Allocator>
0321 class table:table_core_impl<TypePolicy,Hash,Pred,Allocator>
0322 {
0323   using super=table_core_impl<TypePolicy,Hash,Pred,Allocator>;
0324   using type_policy=typename super::type_policy;
0325   using group_type=typename super::group_type;
0326   using super::N;
0327   using prober=typename super::prober;
0328   using arrays_type=typename super::arrays_type;
0329   using size_ctrl_type=typename super::size_ctrl_type;
0330   using locator=typename super::locator;
0331   using compatible_concurrent_table=
0332     concurrent_table<TypePolicy,Hash,Pred,Allocator>;
0333   using group_type_pointer=typename boost::pointer_traits<
0334     typename boost::allocator_pointer<Allocator>::type
0335   >::template rebind<group_type>;
0336   friend compatible_concurrent_table;
0337 
0338 public:
0339   using key_type=typename super::key_type;
0340   using init_type=typename super::init_type;
0341   using value_type=typename super::value_type;
0342   using element_type=typename super::element_type;
0343 
0344 private:
0345   static constexpr bool has_mutable_iterator=
0346     !std::is_same<key_type,value_type>::value;
0347 public:
0348   using hasher=typename super::hasher;
0349   using key_equal=typename super::key_equal;
0350   using allocator_type=typename super::allocator_type;
0351   using pointer=typename super::pointer;
0352   using const_pointer=typename super::const_pointer;
0353   using reference=typename super::reference;
0354   using const_reference=typename super::const_reference;
0355   using size_type=typename super::size_type;
0356   using difference_type=typename super::difference_type;
0357   using const_iterator=table_iterator<type_policy,group_type_pointer,true>;
0358   using iterator=typename std::conditional<
0359     has_mutable_iterator,
0360     table_iterator<type_policy,group_type_pointer,false>,
0361     const_iterator>::type;
0362   using erase_return_type=table_erase_return_type<iterator>;
0363 
0364 #if defined(BOOST_UNORDERED_ENABLE_STATS)
0365   using stats=typename super::stats;
0366 #endif
0367 
0368   table(
0369     std::size_t n=default_bucket_count,const Hash& h_=Hash(),
0370     const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
0371     super{n,h_,pred_,al_}
0372     {}
0373 
0374   table(const table& x)=default;
0375   table(table&& x)=default;
0376   table(const table& x,const Allocator& al_):super{x,al_}{}
0377   table(table&& x,const Allocator& al_):super{std::move(x),al_}{}
0378   table(compatible_concurrent_table&& x):
0379     table(std::move(x),x.exclusive_access()){}
0380   ~table()=default;
0381 
0382   table& operator=(const table& x)=default;
0383   table& operator=(table&& x)=default;
0384 
0385   using super::get_allocator;
0386 
0387   iterator begin()noexcept
0388   {
0389     iterator it{this->arrays.groups(),0,this->arrays.elements()};
0390     if(this->arrays.elements()&&
0391        !(this->arrays.groups()[0].match_occupied()&0x1))++it;
0392     return it;
0393   }
0394 
0395   const_iterator begin()const noexcept
0396                    {return const_cast<table*>(this)->begin();}
0397   iterator       end()noexcept{return {};}
0398   const_iterator end()const noexcept{return const_cast<table*>(this)->end();}
0399   const_iterator cbegin()const noexcept{return begin();}
0400   const_iterator cend()const noexcept{return end();}
0401 
0402   using super::empty;
0403   using super::size;
0404   using super::max_size;
0405 
0406   template<typename... Args>
0407   BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
0408   {
0409     alloc_cted_insert_type<type_policy,Allocator,Args...> x(
0410       this->al(),std::forward<Args>(args)...);
0411     return emplace_impl(type_policy::move(x.value()));
0412   }
0413 
0414   /* Optimization for value_type and init_type, to avoid constructing twice */
0415   template <typename T>
0416   BOOST_FORCEINLINE typename std::enable_if<
0417     detail::is_similar_to_any<T, value_type, init_type>::value,
0418     std::pair<iterator, bool> >::type
0419   emplace(T&& x)
0420   {
0421     return emplace_impl(std::forward<T>(x));
0422   }
0423 
0424   /* Optimizations for maps for (k,v) to avoid eagerly constructing value */
0425   template <typename K, typename V>
0426   BOOST_FORCEINLINE
0427     typename std::enable_if<is_emplace_kv_able<table, K>::value,
0428       std::pair<iterator, bool> >::type
0429     emplace(K&& k, V&& v)
0430   {
0431     alloc_cted_or_fwded_key_type<type_policy, Allocator, K&&> x(
0432       this->al(), std::forward<K>(k));
0433     return emplace_impl(
0434       try_emplace_args_t{}, x.move_or_fwd(), std::forward<V>(v));
0435   }
0436 
0437   template<typename Key,typename... Args>
0438   BOOST_FORCEINLINE std::pair<iterator,bool> try_emplace(
0439     Key&& x,Args&&... args)
0440   {
0441     return emplace_impl(
0442       try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
0443   }
0444 
0445   BOOST_FORCEINLINE std::pair<iterator,bool>
0446   insert(const init_type& x){return emplace_impl(x);}
0447 
0448   BOOST_FORCEINLINE std::pair<iterator,bool>
0449   insert(init_type&& x){return emplace_impl(std::move(x));}
0450 
0451   /* template<typename=void> tilts call ambiguities in favor of init_type */
0452 
0453   template<typename=void>
0454   BOOST_FORCEINLINE std::pair<iterator,bool>
0455   insert(const value_type& x){return emplace_impl(x);}
0456 
0457   template<typename=void>
0458   BOOST_FORCEINLINE std::pair<iterator,bool>
0459   insert(value_type&& x){return emplace_impl(std::move(x));}
0460 
0461   template<typename T=element_type>
0462   BOOST_FORCEINLINE
0463   typename std::enable_if<
0464     !std::is_same<T,value_type>::value,
0465     std::pair<iterator,bool>
0466   >::type
0467   insert(element_type&& x){return emplace_impl(std::move(x));}
0468 
0469   template<
0470     bool dependent_value=false,
0471     typename std::enable_if<
0472       has_mutable_iterator||dependent_value>::type* =nullptr
0473   >
0474   erase_return_type erase(iterator pos)noexcept
0475   {return erase(const_iterator(pos));}
0476 
0477   BOOST_FORCEINLINE
0478   erase_return_type erase(const_iterator pos)noexcept
0479   {
0480     super::erase(pos.pc(),pos.p());
0481     return {pos};
0482   }
0483 
0484   template<typename Key>
0485   BOOST_FORCEINLINE
0486   auto erase(Key&& x) -> typename std::enable_if<
0487     !std::is_convertible<Key,iterator>::value&&
0488     !std::is_convertible<Key,const_iterator>::value, std::size_t>::type
0489   {
0490     auto it=find(x);
0491     if(it!=end()){
0492       erase(it);
0493       return 1;
0494     }
0495     else return 0;
0496   }
0497 
0498   void swap(table& x)
0499     noexcept(noexcept(std::declval<super&>().swap(std::declval<super&>())))
0500   {
0501     super::swap(x);
0502   }
0503 
0504   using super::clear;
0505 
0506   element_type extract(const_iterator pos)
0507   {
0508     BOOST_ASSERT(pos!=end());
0509     erase_on_exit e{*this,pos};
0510     (void)e;
0511     return std::move(*pos.p());
0512   }
0513 
0514   // TODO: should we accept different allocator too?
0515   template<typename Hash2,typename Pred2>
0516   void merge(table<TypePolicy,Hash2,Pred2,Allocator>& x)
0517   {
0518     x.for_all_elements([&,this](group_type* pg,unsigned int n,element_type* p){
0519       erase_on_exit e{x,{pg,n,p}};
0520       if(!emplace_impl(type_policy::move(*p)).second)e.rollback();
0521     });
0522   }
0523 
0524   template<typename Hash2,typename Pred2>
0525   void merge(table<TypePolicy,Hash2,Pred2,Allocator>&& x){merge(x);}
0526 
0527   using super::hash_function;
0528   using super::key_eq;
0529 
0530   template<typename Key>
0531   BOOST_FORCEINLINE iterator find(const Key& x)
0532   {
0533     return make_iterator(super::find(x));
0534   }
0535 
0536   template<typename Key>
0537   BOOST_FORCEINLINE const_iterator find(const Key& x)const
0538   {
0539     return const_cast<table*>(this)->find(x);
0540   }
0541 
0542   using super::capacity;
0543   using super::load_factor;
0544   using super::max_load_factor;
0545   using super::max_load;
0546   using super::rehash;
0547   using super::reserve;
0548 
0549 #if defined(BOOST_UNORDERED_ENABLE_STATS)
0550   using super::get_stats;
0551   using super::reset_stats;
0552 #endif
0553 
0554   template<typename Predicate>
0555   friend std::size_t erase_if(table& x,Predicate& pr)
0556   {
0557     using value_reference=typename std::conditional<
0558       std::is_same<key_type,value_type>::value,
0559       const_reference,
0560       reference
0561     >::type;
0562 
0563     std::size_t s=x.size();
0564     x.for_all_elements(
0565       [&](group_type* pg,unsigned int n,element_type* p){
0566         if(pr(const_cast<value_reference>(type_policy::value_from(*p)))){
0567           x.super::erase(pg,n,p);
0568         }
0569       });
0570     return std::size_t(s-x.size());
0571   }
0572 
0573   friend bool operator==(const table& x,const table& y)
0574   {
0575     return static_cast<const super&>(x)==static_cast<const super&>(y);
0576   }
0577 
0578   friend bool operator!=(const table& x,const table& y){return !(x==y);}
0579 
0580 private:
0581   template<typename ArraysType>
0582   table(compatible_concurrent_table&& x,arrays_holder<ArraysType,Allocator>&& ah):
0583     super{
0584       std::move(x.h()),std::move(x.pred()),std::move(x.al()),
0585       [&x]{return arrays_type{
0586         x.arrays.groups_size_index,x.arrays.groups_size_mask,
0587         to_pointer<group_type_pointer>(
0588           reinterpret_cast<group_type*>(x.arrays.groups())),
0589         x.arrays.elements_};},
0590       size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}}
0591   {
0592     compatible_concurrent_table::arrays_type::delete_group_access(x.al(),x.arrays);
0593     x.arrays=ah.release();
0594     x.size_ctrl.ml=x.initial_max_load();
0595     x.size_ctrl.size=0;
0596     BOOST_UNORDERED_SWAP_STATS(this->cstats,x.cstats);
0597   }
0598 
0599   template<typename ExclusiveLockGuard>
0600   table(compatible_concurrent_table&& x,ExclusiveLockGuard):
0601     table(std::move(x),x.make_empty_arrays())
0602   {}
0603 
0604   struct erase_on_exit
0605   {
0606     erase_on_exit(table& x_,const_iterator it_):x(x_),it(it_){}
0607     ~erase_on_exit(){if(!rollback_)x.erase(it);}
0608 
0609     void rollback(){rollback_=true;}
0610 
0611     table&         x;
0612     const_iterator it;
0613     bool           rollback_=false;
0614   };
0615 
0616   static inline iterator make_iterator(const locator& l)noexcept
0617   {
0618     return {l.pg,l.n,l.p};
0619   }
0620 
0621   template<typename... Args>
0622   BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
0623   {
0624     const auto &k=this->key_from(std::forward<Args>(args)...);
0625     auto        hash=this->hash_for(k);
0626     auto        pos0=this->position_for(hash);
0627     auto        loc=super::find(k,pos0,hash);
0628 
0629     if(loc){
0630       return {make_iterator(loc),false};
0631     }
0632     if(BOOST_LIKELY(this->size_ctrl.size<this->size_ctrl.ml)){
0633       return {
0634         make_iterator(
0635           this->unchecked_emplace_at(pos0,hash,std::forward<Args>(args)...)),
0636         true
0637       };  
0638     }
0639     else{
0640       return {
0641         make_iterator(
0642           this->unchecked_emplace_with_rehash(
0643             hash,std::forward<Args>(args)...)),
0644         true
0645       };  
0646     }
0647   }
0648 };
0649 
0650 #if defined(BOOST_MSVC)
0651 #pragma warning(pop) /* C4714 */
0652 #endif
0653 
0654 #include <boost/unordered/detail/foa/restore_wshadow.hpp>
0655 
0656 } /* namespace foa */
0657 } /* namespace detail */
0658 } /* namespace unordered */
0659 } /* namespace boost */
0660 
0661 #endif