Use nothrow move assignment for function objects, when available.

Originally I was going to use two different versions of `hash_functions`, but
the recent discussion on binary compatibility persuaded me not to.

[SVN r84276]
This commit is contained in:
Daniel James
2013-05-13 23:12:46 +00:00
parent 44f61e5878
commit a422b40041
2 changed files with 63 additions and 19 deletions

View File

@ -15,6 +15,8 @@
#include <boost/unordered/detail/allocate.hpp> #include <boost/unordered/detail/allocate.hpp>
#include <boost/type_traits/aligned_storage.hpp> #include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp> #include <boost/type_traits/alignment_of.hpp>
#include <boost/type_traits/is_nothrow_move_constructible.hpp>
#include <boost/type_traits/is_nothrow_move_assignable.hpp>
#include <boost/swap.hpp> #include <boost/swap.hpp>
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/limits.hpp> #include <boost/limits.hpp>
@ -670,12 +672,16 @@ namespace boost { namespace unordered { namespace detail {
// atomically assigns the new function objects in a strongly // atomically assigns the new function objects in a strongly
// exception safe manner. // exception safe manner.
template <class H, class P> class set_hash_functions; template <class H, class P, bool NoThrowMoveAssign>
class set_hash_functions;
template <class H, class P> template <class H, class P>
class functions class functions
{ {
friend class boost::unordered::detail::set_hash_functions<H, P>; friend class boost::unordered::detail::set_hash_functions<H, P,
boost::is_nothrow_move_assignable<H>::value &&
boost::is_nothrow_move_assignable<P>::value
>;
functions& operator=(functions const&); functions& operator=(functions const&);
typedef compressed<H, P> function_pair; typedef compressed<H, P> function_pair;
@ -692,6 +698,11 @@ namespace boost { namespace unordered { namespace detail {
static_cast<void const*>(&funcs_[current_])); static_cast<void const*>(&funcs_[current_]));
} }
function_pair& current() {
return *static_cast<function_pair*>(
static_cast<void*>(&funcs_[current_]));
}
void construct(bool which, H const& hf, P const& eq) void construct(bool which, H const& hf, P const& eq)
{ {
new((void*) &funcs_[which]) function_pair(hf, eq); new((void*) &funcs_[which]) function_pair(hf, eq);
@ -709,6 +720,11 @@ namespace boost { namespace unordered { namespace detail {
public: public:
typedef boost::unordered::detail::set_hash_functions<H, P,
boost::is_nothrow_move_assignable<H>::value &&
boost::is_nothrow_move_assignable<P>::value
> set_hash_functions;
functions(H const& hf, P const& eq) functions(H const& hf, P const& eq)
: current_(false) : current_(false)
{ {
@ -733,26 +749,28 @@ namespace boost { namespace unordered { namespace detail {
return current().second(); return current().second();
} }
}; };
template <class H, class P> template <class H, class P>
class set_hash_functions class set_hash_functions<H, P, false>
{ {
set_hash_functions(set_hash_functions const&); set_hash_functions(set_hash_functions const&);
set_hash_functions& operator=(set_hash_functions const&); set_hash_functions& operator=(set_hash_functions const&);
typedef functions<H, P> functions_type;
functions<H,P>& functions_; functions_type& functions_;
bool tmp_functions_; bool tmp_functions_;
public: public:
set_hash_functions(functions<H,P>& f, H const& h, P const& p) set_hash_functions(functions_type& f, H const& h, P const& p)
: functions_(f), : functions_(f),
tmp_functions_(!f.current_) tmp_functions_(!f.current_)
{ {
f.construct(tmp_functions_, h, p); f.construct(tmp_functions_, h, p);
} }
set_hash_functions(functions<H,P>& f, functions<H,P> const& other) set_hash_functions(functions_type& f, functions_type const& other)
: functions_(f), : functions_(f),
tmp_functions_(!f.current_) tmp_functions_(!f.current_)
{ {
@ -771,6 +789,37 @@ namespace boost { namespace unordered { namespace detail {
} }
}; };
template <class H, class P>
class set_hash_functions<H, P, true>
{
set_hash_functions(set_hash_functions const&);
set_hash_functions& operator=(set_hash_functions const&);
typedef functions<H, P> functions_type;
functions_type& functions_;
H hash_;
P pred_;
public:
set_hash_functions(functions_type& f, H const& h, P const& p) :
functions_(f),
hash_(h),
pred_(p) {}
set_hash_functions(functions_type& f, functions_type const& other) :
functions_(f),
hash_(other.hash_function()),
pred_(other.key_eq()) {}
void commit()
{
functions_.current().first() = boost::move(hash_);
functions_.current().second() = boost::move(pred_);
}
};
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// rvalue parameters when type can't be a BOOST_RV_REF(T) parameter // rvalue parameters when type can't be a BOOST_RV_REF(T) parameter
// e.g. for int // e.g. for int

View File

@ -159,6 +159,7 @@ namespace boost { namespace unordered { namespace detail {
typedef boost::unordered::detail::functions< typedef boost::unordered::detail::functions<
typename Types::hasher, typename Types::hasher,
typename Types::key_equal> functions; typename Types::key_equal> functions;
typedef typename functions::set_hash_functions set_hash_functions;
typedef typename Types::allocator allocator; typedef typename Types::allocator allocator;
typedef typename boost::unordered::detail:: typedef typename boost::unordered::detail::
@ -469,10 +470,8 @@ namespace boost { namespace unordered { namespace detail {
// Only swaps the allocators if propagate_on_container_swap // Only swaps the allocators if propagate_on_container_swap
void swap(table& x) void swap(table& x)
{ {
boost::unordered::detail::set_hash_functions<hasher, key_equal> set_hash_functions op1(*this, x);
op1(*this, x); set_hash_functions op2(x, *this);
boost::unordered::detail::set_hash_functions<hasher, key_equal>
op2(x, *this);
// I think swap can throw if Propagate::value, // I think swap can throw if Propagate::value,
// since the allocators' swap can throw. Not sure though. // since the allocators' swap can throw. Not sure though.
@ -637,8 +636,7 @@ namespace boost { namespace unordered { namespace detail {
void assign(table const& x, false_type) void assign(table const& x, false_type)
{ {
// Strong exception safety. // Strong exception safety.
boost::unordered::detail::set_hash_functions<hasher, key_equal> set_hash_functions new_func_this(*this, x);
new_func_this(*this, x);
new_func_this.commit(); new_func_this.commit();
mlf_ = x.mlf_; mlf_ = x.mlf_;
recalculate_max_load(); recalculate_max_load();
@ -666,8 +664,7 @@ namespace boost { namespace unordered { namespace detail {
assign(x, false_type()); assign(x, false_type());
} }
else { else {
boost::unordered::detail::set_hash_functions<hasher, key_equal> set_hash_functions new_func_this(*this, x);
new_func_this(*this, x);
// Delete everything with current allocators before assigning // Delete everything with current allocators before assigning
// the new ones. // the new ones.
@ -714,8 +711,7 @@ namespace boost { namespace unordered { namespace detail {
move_assign_no_alloc(x); move_assign_no_alloc(x);
} }
else { else {
boost::unordered::detail::set_hash_functions<hasher, key_equal> set_hash_functions new_func_this(*this, x);
new_func_this(*this, x);
new_func_this.commit(); new_func_this.commit();
mlf_ = x.mlf_; mlf_ = x.mlf_;
recalculate_max_load(); recalculate_max_load();
@ -740,8 +736,7 @@ namespace boost { namespace unordered { namespace detail {
void move_assign_no_alloc(table& x) void move_assign_no_alloc(table& x)
{ {
boost::unordered::detail::set_hash_functions<hasher, key_equal> set_hash_functions new_func_this(*this, x);
new_func_this(*this, x);
// No throw from here. // No throw from here.
mlf_ = x.mlf_; mlf_ = x.mlf_;
max_load_ = x.max_load_; max_load_ = x.max_load_;