diff --git a/include/boost/unordered/detail/fca.hpp b/include/boost/unordered/detail/fca.hpp index a1d14d95..174ff4be 100644 --- a/include/boost/unordered/detail/fca.hpp +++ b/include/boost/unordered/detail/fca.hpp @@ -494,11 +494,25 @@ namespace boost { group_pointer groups; public: + grouped_bucket_array() + : empty_value( + empty_init_t(), node_allocator_type()), + size_index_(0), size_(0), buckets(), groups() + { + } + grouped_bucket_array(size_type n, const Allocator& al) : empty_value(empty_init_t(), al), - size_index_(size_policy::size_index(n)), - size_(size_policy::size(size_index_)), buckets(), groups() + size_index_(0), + size_(0), buckets(), groups() { + if (n == 0) { + return; + } + + size_index_ = size_policy::size_index(n); + size_ = size_policy::size(size_index_); + bucket_allocator_type bucket_alloc = this->get_bucket_allocator(); group_allocator_type group_alloc = this->get_group_allocator(); diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 37323675..fad51a64 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -200,7 +200,7 @@ namespace boost { template struct table; static const float minimum_max_load_factor = 1e-3f; - static const std::size_t default_bucket_count = 11; + static const std::size_t default_bucket_count = 0; struct move_tag { @@ -2075,9 +2075,16 @@ namespace boost { // From 6.3.1/13: // Only resize when size >= mlf_ * count - max_load_ = boost::unordered::detail::double_to_size( - floor(static_cast(mlf_) * - static_cast(buckets_.bucket_count()))); + std::size_t const bc = buckets_.bucket_count(); + + // it's important we do the `bc == 0` check here because the `mlf_` + // can be specified to be infinity. The operation `n * INF` is `INF` + // for all `n > 0` but NaN for `n == 0`. + // + max_load_ = + bc == 0 ? 0 + : boost::unordered::detail::double_to_size(floor( + static_cast(mlf_) * static_cast(bc))); } void max_load_factor(float z) @@ -2090,6 +2097,12 @@ namespace boost { //////////////////////////////////////////////////////////////////////// // Constructors + table() + : functions(hasher(), key_equal()), size_(0), mlf_(1.0f), + max_load_(0) + { + } + table(std::size_t num_buckets, hasher const& hf, key_equal const& eq, node_allocator_type const& a) : functions(hf, eq), size_(0), mlf_(1.0f), max_load_(0), @@ -2361,7 +2374,10 @@ namespace boost { template void move_assign(table& x, UniqueType is_unique, false_type) { - reserve(x.size_); + if (x.size_ > 0) { + reserve(x.size_); + } + if (node_alloc() == x.node_alloc()) { move_assign_equal_alloc(x); } else { @@ -2390,7 +2406,9 @@ namespace boost { { mlf_ = x.mlf_; recalculate_max_load(); - this->reserve_for_insert(x.size_); + if (x.size_ > 0) { + this->reserve_for_insert(x.size_); + } this->clear_impl(); } BOOST_CATCH(...) diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 3e25a28a..d66c5394 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -1664,8 +1664,6 @@ namespace boost { template unordered_map::unordered_map() - : table_(boost::unordered::detail::default_bucket_count, hasher(), - key_equal(), allocator_type()) { } @@ -2147,8 +2145,6 @@ namespace boost { template unordered_multimap::unordered_multimap() - : table_(boost::unordered::detail::default_bucket_count, hasher(), - key_equal(), allocator_type()) { } diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 82d323c6..ed736c21 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -1266,8 +1266,6 @@ namespace boost { //////////////////////////////////////////////////////////////////////////// template unordered_set::unordered_set() - : table_(boost::unordered::detail::default_bucket_count, hasher(), - key_equal(), allocator_type()) { } @@ -1664,8 +1662,6 @@ namespace boost { template unordered_multiset::unordered_multiset() - : table_(boost::unordered::detail::default_bucket_count, hasher(), - key_equal(), allocator_type()) { } diff --git a/test/exception/insert_exception_tests.cpp b/test/exception/insert_exception_tests.cpp index 79074c5e..0f98e0c4 100644 --- a/test/exception/insert_exception_tests.cpp +++ b/test/exception/insert_exception_tests.cpp @@ -95,7 +95,7 @@ void insert_rehash_exception_test( T*, Inserter insert, test::random_generator gen) { for (int i = 0; i < 5; ++i) { - T x; + T x(1); rehash_prep(x); test::random_values v2(5, gen); @@ -393,7 +393,7 @@ template void insert_range_rehash_exception_test(T*, test::random_generator gen) { for (int i = 0; i < 5; ++i) { - T x; + T x(1); rehash_prep(x); test::random_values v2(5, gen); diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp index 23042b55..fddb40c3 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_tests.cpp @@ -45,6 +45,7 @@ namespace assign_tests { BOOST_TEST(x.empty()); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::detail::tracker.count_allocations == 0); } BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests1.2\n"; @@ -94,6 +95,7 @@ namespace assign_tests { BOOST_TEST(test::equivalent(x1.key_eq(), eq1)); BOOST_TEST(test::equivalent(x2.hash_function(), hf1)); BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); + BOOST_TEST(test::detail::tracker.count_allocations == 0); test::check_container(x1, x2); } diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 5f571986..c2a43283 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -35,6 +35,7 @@ namespace constructor_tests { T x(0, hf, eq); BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.bucket_count(), 0u); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); @@ -73,6 +74,7 @@ namespace constructor_tests { T x; BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.bucket_count(), 0u); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); @@ -140,6 +142,7 @@ namespace constructor_tests { T x(0, hf, eq, al); BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.bucket_count(), 0u); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); @@ -166,6 +169,7 @@ namespace constructor_tests { T x(al); BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.bucket_count(), 0u); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); @@ -325,6 +329,7 @@ namespace constructor_tests { T x(list); BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.bucket_count(), 0u); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); @@ -380,6 +385,104 @@ namespace constructor_tests { #endif } + template + void no_alloc_default_construct_test(T*, test::random_generator) + { + UNORDERED_SUB_TEST("Construct 1") + { + T x; + BOOST_TEST_EQ(x.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); + } + + UNORDERED_SUB_TEST("Construct 2") + { + { + T x(0); + BOOST_TEST_EQ(x.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); + } + + { + T x(1); + BOOST_TEST_GT(x.bucket_count(), 0u); + BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u); + } + } + + UNORDERED_SUB_TEST("Construct 3") + { + test::random_values v; + T x(v.begin(), v.end()); + BOOST_TEST_EQ(x.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); + } + + UNORDERED_SUB_TEST("Construct 4") + { + { + test::random_values v; + T x(v.begin(), v.end(), 0); + BOOST_TEST_EQ(x.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); + } + + { + test::random_values v; + T x(v.begin(), v.end(), 1); + BOOST_TEST_GT(x.bucket_count(), 0u); + BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u); + } + } + + UNORDERED_SUB_TEST("Construct 5") + { + typename T::allocator_type al; + + { + T x(al); + BOOST_TEST_EQ(x.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); + } + } + + UNORDERED_SUB_TEST("Construct 6") + { + typename T::allocator_type al; + + T x(0, al); + BOOST_TEST_EQ(x.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); + } + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + UNORDERED_SUB_TEST("Initializer list 1") + { + std::initializer_list list; + T x(list); + BOOST_TEST_EQ(x.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); + } + + UNORDERED_SUB_TEST("Initializer list 2") + { + { + std::initializer_list list; + T x(list, 0); + BOOST_TEST_EQ(x.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); + } + + { + std::initializer_list list; + T x(list, 1); + BOOST_TEST_GT(x.bucket_count(), 0u); + BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u); + } + } +#endif + } + template void map_constructor_test(T*, test::random_generator const& generator) { @@ -422,6 +525,10 @@ namespace constructor_tests { ((test_map_std_alloc)(test_map)(test_multimap))( (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(no_alloc_default_construct_test, + ((test_set)(test_multiset)(test_map)(test_multimap))( + (default_generator)(generate_collisions)(limited_range))) + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) UNORDERED_AUTO_TEST (test_default_initializer_list) { diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp index 1f1ac8f5..f7c0ddd8 100644 --- a/test/unordered/copy_tests.cpp +++ b/test/unordered/copy_tests.cpp @@ -43,6 +43,23 @@ namespace copy_tests { BOOST_TEST(x.max_load_factor() == y.max_load_factor()); BOOST_TEST(test::selected_count(y.get_allocator()) == (allocator_type::is_select_on_copy)); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + test::check_equivalent_keys(y); + } + + { + test::check_instances check_; + + T x(0); + T y(x); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::selected_count(y.get_allocator()) == + (allocator_type::is_select_on_copy)); + BOOST_TEST(test::detail::tracker.count_allocations == 0); test::check_equivalent_keys(y); } @@ -91,6 +108,22 @@ namespace copy_tests { typedef typename T::allocator_type allocator_type; + { + test::check_instances check_; + + T x(0, hf, eq, al); + T y(x); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::selected_count(y.get_allocator()) == + (allocator_type::is_select_on_copy)); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + test::check_equivalent_keys(y); + } + { test::check_instances check_; @@ -106,6 +139,21 @@ namespace copy_tests { test::check_equivalent_keys(y); } + { + test::check_instances check_; + + T x(0, hf, eq, al); + T y(x, al2); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al2)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::selected_count(y.get_allocator()) == 0); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + test::check_equivalent_keys(y); + } + { test::check_instances check_; @@ -120,6 +168,22 @@ namespace copy_tests { test::check_equivalent_keys(y); } + { + test::check_instances check_; + + test::random_values v; + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x); + test::unordered_equivalence_tester equivalent(x); + BOOST_TEST(equivalent(y)); + test::check_equivalent_keys(y); + BOOST_TEST(test::selected_count(y.get_allocator()) == + (allocator_type::is_select_on_copy)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + } + { test::check_instances check_; @@ -135,6 +199,21 @@ namespace copy_tests { BOOST_TEST(test::equivalent(y.get_allocator(), al)); } + { + test::check_instances check_; + + test::random_values v; + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x, al2); + test::unordered_equivalence_tester equivalent(x); + BOOST_TEST(equivalent(y)); + test::check_equivalent_keys(y); + BOOST_TEST(test::selected_count(y.get_allocator()) == 0); + BOOST_TEST(test::equivalent(y.get_allocator(), al2)); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + } + { test::check_instances check_; diff --git a/test/unordered/move_tests.cpp b/test/unordered/move_tests.cpp index f2afbf68..21b2e90c 100644 --- a/test/unordered/move_tests.cpp +++ b/test/unordered/move_tests.cpp @@ -73,6 +73,10 @@ namespace move_tests { BOOST_TEST(test::equivalent(y.get_allocator(), al)); BOOST_TEST(y.max_load_factor() == 1.0); test::check_equivalent_keys(y); +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); +#endif } { @@ -90,7 +94,7 @@ namespace move_tests { } template - void move_assign_tests1(T*, test::random_generator const& generator) + void move_assign_tests1(T* p, test::random_generator const& generator) { { test::check_instances check_; @@ -101,6 +105,19 @@ namespace move_tests { y = create(v, count); #if BOOST_UNORDERED_TEST_MOVING && defined(BOOST_HAS_NRVO) BOOST_TEST(count == test::global_object_count); +#endif + test::check_container(y, v); + test::check_equivalent_keys(y); + } + + { + test::random_values v; + + T y; + y = empty(p); +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); #endif test::check_container(y, v); test::check_equivalent_keys(y); @@ -236,6 +253,32 @@ namespace move_tests { #endif } + { + test::random_values v; + T y(0, hf, eq, al1); + T x(0, hf, eq, al2); + x.max_load_factor(0.25); + + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); + + y = boost::move(x); +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); +#endif + test::check_container(y, v); + test::check_equivalent_keys(y); + BOOST_TEST(y.max_load_factor() == 0.25); + + if (BOOST_UNORDERED_TEST_MOVING + ? (bool)allocator_type::is_propagate_on_move + : (bool)allocator_type::is_propagate_on_assign) { + BOOST_TEST(test::equivalent(y.get_allocator(), al2)); + } else { + BOOST_TEST(test::equivalent(y.get_allocator(), al1)); + } + } + { test::check_instances check_; @@ -701,12 +744,18 @@ namespace move_tests { test::random_values const v(1000, generator); test::object_count count; T y(create(v, count)); + + unsigned num_allocs = test::detail::tracker.count_allocations; + (void)num_allocs; + T x(boost::move(y)); #if defined(BOOST_UNORDERED_USE_MOVE) || \ !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) BOOST_TEST(y.empty()); BOOST_TEST(y.begin() == y.end()); + BOOST_TEST_EQ(y.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); #endif fps[i](y, v); @@ -743,6 +792,10 @@ namespace move_tests { test::random_values v(1000, generator); test::object_count count; T y(create(v, count)); + + unsigned num_allocs = test::detail::tracker.count_allocations; + (void)num_allocs; + T x(empty(ptr)); x = boost::move(y); @@ -750,6 +803,8 @@ namespace move_tests { !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) BOOST_TEST(y.empty()); BOOST_TEST(y.begin() == y.end()); + BOOST_TEST_EQ(y.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); #endif fps[i](y, v); @@ -769,17 +824,23 @@ namespace move_tests { test::random_values v(1000, generator); test::object_count count; T y(v.begin(), v.end(), 0, hf, eq, al1); + + unsigned num_allocs = test::detail::tracker.count_allocations; + (void)num_allocs; + T x(al2); x = boost::move(y); bool b = boost::allocator_propagate_on_container_move_assignment< typename T::allocator_type>::type::value; if (b) { - #if defined(BOOST_UNORDERED_USE_MOVE) || \ +#if defined(BOOST_UNORDERED_USE_MOVE) || \ !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - BOOST_TEST(y.empty()); - BOOST_TEST(y.begin() == y.end()); - #else + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); + BOOST_TEST_EQ(y.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); +#else BOOST_TEST_NOT(y.empty()); BOOST_TEST(y.begin() != y.end()); diff --git a/test/unordered/scary_tests.cpp b/test/unordered/scary_tests.cpp index f671f68e..383af13a 100644 --- a/test/unordered/scary_tests.cpp +++ b/test/unordered/scary_tests.cpp @@ -224,7 +224,7 @@ template void scary_test() typename C2::const_iterator cbegin(x.cbegin()); BOOST_TEST(cbegin == x.cend()); - BOOST_ASSERT(x.bucket_count() > 0); + BOOST_TEST_EQ(x.bucket_count(), 0u); typename C2::local_iterator lbegin(x.begin(0)); BOOST_TEST(lbegin == x.end(0));