Swap noexcept support

Not properly supported as we don't have is_nothrow_swappable yet.
This commit is contained in:
Daniel James
2018-01-05 17:48:13 +00:00
parent 7e28fdd45a
commit 5854090dc7
4 changed files with 127 additions and 40 deletions

View File

@ -31,12 +31,15 @@
#include <boost/type_traits/add_lvalue_reference.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/type_traits/is_empty.hpp>
#include <boost/type_traits/is_nothrow_move_assignable.hpp>
#include <boost/type_traits/is_nothrow_move_constructible.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_scalar.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <boost/utility/addressof.hpp>
@ -751,6 +754,19 @@ namespace boost {
#pragma warning(pop)
#endif
////////////////////////////////////////////////////////////////////////////
// is_nothrow_swappable
//
// TODO: Replace this very basic implementation when the full type_traits
// implementation is available.
template <class T>
struct is_nothrow_swappable
: boost::integral_constant<bool,
boost::is_scalar<T>::value && !boost::is_const<T>::value>
{
};
//////////////////////////////////////////////////////////////////////////
// value_base
//
@ -2756,6 +2772,9 @@ namespace boost {
static const bool nothrow_move_constructible =
boost::is_nothrow_move_constructible<H>::value &&
boost::is_nothrow_move_constructible<P>::value;
static const bool nothrow_swappable =
boost::unordered::detail::is_nothrow_swappable<H>::value &&
boost::unordered::detail::is_nothrow_swappable<P>::value;
private:
friend class boost::unordered::detail::set_hash_functions<H, P,
@ -3279,14 +3298,12 @@ namespace boost {
allocators_.swap(other.allocators_);
}
// Only swaps the allocators if propagate_on_container_swap
void swap(table& x)
// Not nothrow swappable
void swap(table& x, false_type)
{
set_hash_functions op1(*this, x);
set_hash_functions op2(x, *this);
// I think swap can throw if Propagate::value,
// since the allocators' swap can throw. Not sure though.
swap_allocators(
x, boost::unordered::detail::integral_constant<bool,
allocator_traits<
@ -3301,6 +3318,34 @@ namespace boost {
op2.commit();
}
// Nothrow swappable
void swap(table& x, true_type)
{
swap_allocators(
x, boost::unordered::detail::integral_constant<bool,
allocator_traits<
node_allocator>::propagate_on_container_swap::value>());
boost::swap(buckets_, x.buckets_);
boost::swap(bucket_count_, x.bucket_count_);
boost::swap(size_, x.size_);
std::swap(mlf_, x.mlf_);
std::swap(max_load_, x.max_load_);
this->current_functions().swap(x.current_functions());
}
// Only swaps the allocators if propagate_on_container_swap.
// If not propagate_on_container_swap and allocators aren't
// equal, behaviour is undefined.
void swap(table& x)
{
BOOST_ASSERT(allocator_traits<
node_allocator>::propagate_on_container_swap::value ||
node_alloc() == x.node_alloc());
swap(x, boost::unordered::detail::integral_constant<bool,
functions::nothrow_swappable>());
}
// Only call with nodes allocated with the currect allocator, or
// one that is equal to it. (Can't assert because other's
// allocators might have already been moved).

View File

@ -715,11 +715,10 @@ namespace boost {
BOOST_UNORDERED_DEPRECATED("Use erase instead")
void erase_return_void(const_iterator it) { erase(it); }
void swap(unordered_map&);
// C++17 support: BOOST_NOEXCEPT_IF(
// value_allocator_traits::is_always_equal::value &&
// is_nothrow_move_assignable_v<H> &&
// is_nothrow_move_assignable_v<P>)
void swap(unordered_map&)
BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&&
boost::unordered::detail::is_nothrow_swappable<H>::value&&
boost::unordered::detail::is_nothrow_swappable<P>::value);
void clear() BOOST_NOEXCEPT { table_.clear_impl(); }
template <typename H2, typename P2>
@ -1327,11 +1326,10 @@ namespace boost {
BOOST_UNORDERED_DEPRECATED("Use erase instead")
void erase_return_void(const_iterator it) { erase(it); }
void swap(unordered_multimap&);
// C++17 support: BOOST_NOEXCEPT_IF(
// value_allocator_traits::is_always_equal::value &&
// is_nothrow_move_assignable_v<H> &&
// is_nothrow_move_assignable_v<P>)
void swap(unordered_multimap&)
BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&&
boost::unordered::detail::is_nothrow_swappable<H>::value&&
boost::unordered::detail::is_nothrow_swappable<P>::value);
void clear() BOOST_NOEXCEPT { table_.clear_impl(); }
template <typename H2, typename P2>
@ -1740,10 +1738,9 @@ namespace boost {
template <class K, class T, class H, class P, class A>
void unordered_map<K, T, H, P, A>::swap(unordered_map& other)
// C++17 support: BOOST_NOEXCEPT_IF(
// value_allocator_traits::is_always_equal::value &&
// is_nothrow_move_assignable_v<H> &&
// is_nothrow_move_assignable_v<P>)
BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&&
boost::unordered::detail::is_nothrow_swappable<H>::value&&
boost::unordered::detail::is_nothrow_swappable<P>::value)
{
table_.swap(other.table_);
}
@ -2217,10 +2214,9 @@ namespace boost {
template <class K, class T, class H, class P, class A>
void unordered_multimap<K, T, H, P, A>::swap(unordered_multimap& other)
// C++17 support: BOOST_NOEXCEPT_IF(
// value_allocator_traits::is_always_equal::value &&
// is_nothrow_move_assignable_v<H> &&
// is_nothrow_move_assignable_v<P>)
BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&&
boost::unordered::detail::is_nothrow_swappable<H>::value&&
boost::unordered::detail::is_nothrow_swappable<P>::value)
{
table_.swap(other.table_);
}

View File

@ -441,11 +441,10 @@ namespace boost {
BOOST_UNORDERED_DEPRECATED("Use erase instead")
void erase_return_void(const_iterator it) { erase(it); }
void swap(unordered_set&);
// C++17 support: BOOST_NOEXCEPT_IF(
// value_allocator_traits::is_always_equal::value &&
// is_nothrow_move_assignable_v<H> &&
// is_nothrow_move_assignable_v<P>)
void swap(unordered_set&)
BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&&
boost::unordered::detail::is_nothrow_swappable<H>::value&&
boost::unordered::detail::is_nothrow_swappable<P>::value);
void clear() BOOST_NOEXCEPT { table_.clear_impl(); }
template <typename H2, typename P2>
@ -991,11 +990,10 @@ namespace boost {
BOOST_UNORDERED_DEPRECATED("Use erase instead")
void erase_return_void(const_iterator it) { erase(it); }
void swap(unordered_multiset&);
// C++17 support: BOOST_NOEXCEPT_IF(
// value_allocator_traits::is_always_equal::value &&
// is_nothrow_move_assignable_v<H> &&
// is_nothrow_move_assignable_v<P>)
void swap(unordered_multiset&)
BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&&
boost::unordered::detail::is_nothrow_swappable<H>::value&&
boost::unordered::detail::is_nothrow_swappable<P>::value);
void clear() BOOST_NOEXCEPT { table_.clear_impl(); }
template <typename H2, typename P2>
@ -1365,10 +1363,9 @@ namespace boost {
template <class T, class H, class P, class A>
void unordered_set<T, H, P, A>::swap(unordered_set& other)
// C++17 support: BOOST_NOEXCEPT_IF(
// value_allocator_traits::is_always_equal::value &&
// is_nothrow_move_assignable_v<H> &&
// is_nothrow_move_assignable_v<P>)
BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&&
boost::unordered::detail::is_nothrow_swappable<H>::value&&
boost::unordered::detail::is_nothrow_swappable<P>::value)
{
table_.swap(other.table_);
}
@ -1760,10 +1757,9 @@ namespace boost {
template <class T, class H, class P, class A>
void unordered_multiset<T, H, P, A>::swap(unordered_multiset& other)
// C++17 support: BOOST_NOEXCEPT_IF(
// value_allocator_traits::is_always_equal::value &&
// is_nothrow_move_assignable_v<H> &&
// is_nothrow_move_assignable_v<P>)
BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&&
boost::unordered::detail::is_nothrow_swappable<H>::value&&
boost::unordered::detail::is_nothrow_swappable<P>::value)
{
table_.swap(other.table_);
}

View File

@ -89,6 +89,7 @@ namespace noexcept_tests {
};
typedef hash_nothrow<true, false, false> hash_nothrow_move;
typedef hash_nothrow<false, false, true> hash_nothrow_swap;
template <bool nothrow_move_construct, bool nothrow_move_assign,
bool nothrow_swap>
@ -137,14 +138,18 @@ namespace noexcept_tests {
};
typedef equal_to_nothrow<true, false, false> equal_to_nothrow_move;
typedef equal_to_nothrow<false, false, true> equal_to_nothrow_swap;
bool have_is_nothrow_move = false;
bool have_is_nothrow_swap = false;
UNORDERED_AUTO_TEST (check_is_nothrow_move) {
BOOST_TEST(
!boost::is_nothrow_move_constructible<hash_possible_exception>::value);
have_is_nothrow_move =
boost::is_nothrow_move_constructible<hash_nothrow_move>::value;
have_is_nothrow_swap =
boost::unordered::detail::is_nothrow_swappable<hash_nothrow_swap>::value;
// Copied from boost::is_nothrow_move_constructible implementation
// to make sure this does actually detect it when expected.
@ -156,6 +161,17 @@ namespace noexcept_tests {
!BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40800)
BOOST_TEST(have_is_nothrow_move);
#endif
#if !defined(BOOST_NO_SFINAE_EXPR) && !defined(BOOST_NO_CXX11_NOEXCEPT) && \
!defined(BOOST_NO_CXX11_DECLTYPE) && \
!defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS)
// TODO: Turn test on when unordered starts to use is_nothrow_swap
// BOOST_TEST(have_is_nothrow_swap);
#endif
BOOST_LIGHTWEIGHT_TEST_OSTREAM
<< "have_is_nothrow_move: " << have_is_nothrow_move << std::endl
<< "have_is_nothrow_swap: " << have_is_nothrow_swap << std::endl;
}
UNORDERED_AUTO_TEST (test_noexcept) {
@ -203,6 +219,40 @@ namespace noexcept_tests {
throwing_test_exception = false;
}
}
UNORDERED_AUTO_TEST (test_nothrow_swap_when_noexcept) {
typedef boost::unordered_set<int, hash_nothrow_swap, equal_to_nothrow_swap>
throwing_set;
if (have_is_nothrow_swap) {
BOOST_TEST(
boost::unordered::detail::is_nothrow_swappable<throwing_set>::value);
}
throwing_test_exception = false;
throwing_set x1;
throwing_set x2;
x1.insert(10);
x1.insert(50);
for (int i = 0; i < 100; ++i) {
x2.insert(i);
}
try {
throwing_test_exception = true;
x1.swap(x2);
BOOST_TEST(x1.size() == 100);
BOOST_TEST(x2.size() == 2);
BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50);
BOOST_TEST(have_is_nothrow_swap);
} catch (test_exception) {
BOOST_TEST(!have_is_nothrow_swap);
}
throwing_test_exception = false;
}
}
RUN_TESTS()