diff --git a/include/tsl/bhopscotch_map.h b/include/tsl/bhopscotch_map.h index 1d4833e..3e450c6 100644 --- a/include/tsl/bhopscotch_map.h +++ b/include/tsl/bhopscotch_map.h @@ -119,7 +119,8 @@ class bhopscotch_map { /* * Constructors */ - bhopscotch_map() : bhopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {} + bhopscotch_map() noexcept(std::is_nothrow_default_constructible::value) + : m_ht() {} explicit bhopscotch_map(size_type bucket_count, const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), @@ -414,7 +415,7 @@ class bhopscotch_map { return m_ht.erase(key, precalculated_hash); } - void swap(bhopscotch_map& other) { other.m_ht.swap(m_ht); } + void swap(bhopscotch_map& other) noexcept(noexcept(other.m_ht.swap(m_ht))) { other.m_ht.swap(m_ht); } /* * Lookup @@ -785,7 +786,7 @@ class bhopscotch_map { return !operator==(lhs, rhs); } - friend void swap(bhopscotch_map& lhs, bhopscotch_map& rhs) { lhs.swap(rhs); } + friend void swap(bhopscotch_map& lhs, bhopscotch_map& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } private: ht m_ht; diff --git a/include/tsl/bhopscotch_set.h b/include/tsl/bhopscotch_set.h index d89cb90..e9d76ac 100644 --- a/include/tsl/bhopscotch_set.h +++ b/include/tsl/bhopscotch_set.h @@ -102,7 +102,8 @@ class bhopscotch_set { /* * Constructors */ - bhopscotch_set() : bhopscotch_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {} + bhopscotch_set() noexcept(std::is_nothrow_default_constructible::value) + : m_ht() {} explicit bhopscotch_set(size_type bucket_count, const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), @@ -309,7 +310,7 @@ class bhopscotch_set { return m_ht.erase(key, precalculated_hash); } - void swap(bhopscotch_set& other) { other.m_ht.swap(m_ht); } + void swap(bhopscotch_set& other) noexcept(noexcept(other.m_ht.swap(m_ht))) { other.m_ht.swap(m_ht); } /* * Lookup @@ -597,7 +598,7 @@ class bhopscotch_set { return !operator==(lhs, rhs); } - friend void swap(bhopscotch_set& lhs, bhopscotch_set& rhs) { lhs.swap(rhs); } + friend void swap(bhopscotch_set& lhs, bhopscotch_set& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } private: ht m_ht; diff --git a/include/tsl/hopscotch_growth_policy.h b/include/tsl/hopscotch_growth_policy.h index 80b5b01..98ec5f4 100644 --- a/include/tsl/hopscotch_growth_policy.h +++ b/include/tsl/hopscotch_growth_policy.h @@ -399,6 +399,19 @@ class prime_growth_policy { "The type of m_iprime is not big enough."); }; +/** + * SFINAE helper to detect growth policies which can be noexcept-initialized + * with a zero min bucket count. + */ +template +struct is_noexcept_on_zero_init : std::false_type {}; +template +struct is_noexcept_on_zero_init> : std::true_type {}; +template +struct is_noexcept_on_zero_init> : std::true_type {}; +template<> +struct is_noexcept_on_zero_init : std::true_type {}; + } // namespace hh } // namespace tsl diff --git a/include/tsl/hopscotch_hash.h b/include/tsl/hopscotch_hash.h index c33dba3..8b10f78 100644 --- a/include/tsl/hopscotch_hash.h +++ b/include/tsl/hopscotch_hash.h @@ -593,6 +593,17 @@ class hopscotch_hash : private Hash, private KeyEqual, private GrowthPolicy { }; public: + template < + class OC = OverflowContainer, + typename std::enable_if::value>::type* = nullptr> + hopscotch_hash() noexcept(DEFAULT_INIT_BUCKETS_SIZE == 0 && + std::is_nothrow_default_constructible::value && + std::is_nothrow_default_constructible::value && + std::is_nothrow_default_constructible::value && + (std::is_nothrow_constructible::value || + hh::is_noexcept_on_zero_init::value)) + : hopscotch_hash(DEFAULT_INIT_BUCKETS_SIZE, Hash(), KeyEqual(), Allocator(), DEFAULT_MAX_LOAD_FACTOR) {} + template < class OC = OverflowContainer, typename std::enable_if::value>::type* = nullptr> @@ -630,6 +641,17 @@ class hopscotch_hash : private Hash, private KeyEqual, private GrowthPolicy { "move constructible."); } + template < + class OC = OverflowContainer, + typename std::enable_if::value>::type* = nullptr> + hopscotch_hash() noexcept(DEFAULT_INIT_BUCKETS_SIZE == 0 && + std::is_nothrow_default_constructible::value && + std::is_nothrow_default_constructible::value && + std::is_nothrow_default_constructible::value && + (std::is_nothrow_constructible::value || + hh::is_noexcept_on_zero_init::value)) + : hopscotch_hash(DEFAULT_INIT_BUCKETS_SIZE, Hash(), KeyEqual(), Allocator(), DEFAULT_MAX_LOAD_FACTOR, typename OC::key_compare()) {} + template < class OC = OverflowContainer, typename std::enable_if::value>::type* = nullptr> @@ -730,7 +752,7 @@ class hopscotch_hash : private Hash, private KeyEqual, private GrowthPolicy { return *this; } - hopscotch_hash& operator=(hopscotch_hash&& other) { + hopscotch_hash& operator=(hopscotch_hash&& other) noexcept(noexcept(other.swap(*this))) { other.swap(*this); other.clear(); @@ -1035,7 +1057,11 @@ class hopscotch_hash : private Hash, private KeyEqual, private GrowthPolicy { return 0; } - void swap(hopscotch_hash& other) { + void swap(hopscotch_hash& other) noexcept(std::is_nothrow_swappable::value && + std::is_nothrow_swappable::value && + std::is_nothrow_swappable::value && + std::is_nothrow_swappable::value && + std::is_nothrow_swappable::value) { using std::swap; swap(static_cast(*this), static_cast(other)); diff --git a/include/tsl/hopscotch_map.h b/include/tsl/hopscotch_map.h index 5efee2d..539a350 100644 --- a/include/tsl/hopscotch_map.h +++ b/include/tsl/hopscotch_map.h @@ -135,7 +135,8 @@ class hopscotch_map { /* * Constructors */ - hopscotch_map() : hopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {} + hopscotch_map() noexcept(std::is_nothrow_default_constructible::value) + : m_ht() {} explicit hopscotch_map(size_type bucket_count, const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), @@ -424,7 +425,7 @@ class hopscotch_map { return m_ht.erase(key, precalculated_hash); } - void swap(hopscotch_map& other) { other.m_ht.swap(m_ht); } + void swap(hopscotch_map& other) noexcept(noexcept(other.m_ht.swap(m_ht))) { other.m_ht.swap(m_ht); } /* * Lookup @@ -783,7 +784,7 @@ class hopscotch_map { return !operator==(lhs, rhs); } - friend void swap(hopscotch_map& lhs, hopscotch_map& rhs) { lhs.swap(rhs); } + friend void swap(hopscotch_map& lhs, hopscotch_map& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } private: ht m_ht; diff --git a/include/tsl/hopscotch_set.h b/include/tsl/hopscotch_set.h index a13488d..5cd0a37 100644 --- a/include/tsl/hopscotch_set.h +++ b/include/tsl/hopscotch_set.h @@ -123,7 +123,8 @@ class hopscotch_set { /* * Constructors */ - hopscotch_set() : hopscotch_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {} + hopscotch_set() noexcept(std::is_nothrow_default_constructible::value) + : m_ht() {} explicit hopscotch_set(size_type bucket_count, const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), @@ -323,7 +324,7 @@ class hopscotch_set { return m_ht.erase(key, precalculated_hash); } - void swap(hopscotch_set& other) { other.m_ht.swap(m_ht); } + void swap(hopscotch_set& other) noexcept(noexcept(other.m_ht.swap(m_ht))) { other.m_ht.swap(m_ht); } /* * Lookup @@ -600,7 +601,7 @@ class hopscotch_set { return !operator==(lhs, rhs); } - friend void swap(hopscotch_set& lhs, hopscotch_set& rhs) { lhs.swap(rhs); } + friend void swap(hopscotch_set& lhs, hopscotch_set& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } private: ht m_ht; diff --git a/tests/hopscotch_map_tests.cpp b/tests/hopscotch_map_tests.cpp index 27cc25c..48c344b 100644 --- a/tests/hopscotch_map_tests.cpp +++ b/tests/hopscotch_map_tests.cpp @@ -1510,4 +1510,11 @@ BOOST_AUTO_TEST_CASE(test_precalculated_hash) { BOOST_CHECK_EQUAL(map.erase(4, map.hash_function()(2)), 0); } +BOOST_AUTO_TEST_CASE_TEMPLATE(test_noexcept, HSet, test_types) { + static_assert(std::is_nothrow_default_constructible::value, ""); + static_assert(std::is_nothrow_move_constructible::value, ""); + static_assert(std::is_nothrow_move_assignable::value, ""); + static_assert(std::is_nothrow_swappable::value, ""); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/hopscotch_set_tests.cpp b/tests/hopscotch_set_tests.cpp index 8ff05ce..61fdb5d 100644 --- a/tests/hopscotch_set_tests.cpp +++ b/tests/hopscotch_set_tests.cpp @@ -163,4 +163,11 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_insert_transparent_hint, HSet, BOOST_CHECK_EQUAL(*otherIt, 2); } +BOOST_AUTO_TEST_CASE_TEMPLATE(test_noexcept, HSet, test_types) { + static_assert(std::is_nothrow_default_constructible::value, ""); + static_assert(std::is_nothrow_move_constructible::value, ""); + static_assert(std::is_nothrow_move_assignable::value, ""); + static_assert(std::is_nothrow_swappable::value, ""); +} + BOOST_AUTO_TEST_SUITE_END()