diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index b0fa97d0..802ab66f 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1386,14 +1386,49 @@ public: template BOOST_FORCEINLINE std::pair emplace(Args&&... args) { - using emplace_type = typename std::conditional< + /* We dispatch based on whether or not the value_type is constructible from + * an rvalue refernce to the deduced emplace_type. We do this specifically + * for the csae of the node-based containers. To this end, we're able to + * avoid allocating a node when a duplicate element is attempted to be + * inserted. For immovable types, we instead dispatch to the routine that + * unconditionally allocates via `type_policy::construct()`. + */ + return emplace_dispatch( std::is_constructible< - init_type, Args... - >::value, - init_type, - value_type - >::type; - return emplace_impl(emplace_type(std::forward(args)...)); + value_type, + emplace_type&&>{}, + std::forward(args)...); + } + + template + BOOST_FORCEINLINE std::pair emplace_dispatch( + std::true_type,Args&&... args + ) { + using emplace_type_t = emplace_type; + return emplace_impl(emplace_type_t(std::forward(args)...)); + } + + template + BOOST_FORCEINLINE std::pair emplace_dispatch( + std::false_type,Args&&... args + ) { + struct guard { + table& t; + element_type& x; + bool destroy; + ~guard(){ + if(destroy) { + type_policy::destroy(t.al(),&x); + } + } + }; + + element_type x; + type_policy::construct(al(),&x,std::forward(args)...); + guard g{*this,x,true}; + auto itp=emplace_impl(type_policy::move(x)); + g.destroy=!itp.second; + return itp; } template @@ -1562,6 +1597,15 @@ private: template friend class table; using arrays_type=table_arrays; + template + using emplace_type = typename std::conditional< + std::is_constructible< + init_type,Args... + >::value, + init_type, + value_type + >::type; + struct clear_on_exit { ~clear_on_exit(){x.clear();}