From 81897a64691dc1007b1bd5672a9ed2c42f9466ad Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 11 Oct 2012 17:29:19 +0000 Subject: [PATCH] Unordered: Fix erasing ranges, and some tests. Fixes #7471 [SVN r80958] --- doc/changes.qbk | 4 + include/boost/unordered/detail/equivalent.hpp | 4 +- include/boost/unordered/detail/table.hpp | 11 +- test/objects/test.hpp | 120 ++++++++++++++++++ test/unordered/erase_equiv_tests.cpp | 26 ++++ test/unordered/erase_tests.cpp | 14 ++ test/unordered/insert_tests.cpp | 97 ++++++++++++-- test/unordered/rehash_tests.cpp | 64 ++++++++-- test/unordered/unnecessary_copy_tests.cpp | 56 +++++++- 9 files changed, 368 insertions(+), 28 deletions(-) diff --git a/doc/changes.qbk b/doc/changes.qbk index d09360a2..f4e42f63 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -3,6 +3,9 @@ / Distributed under the Boost Software License, Version 1.0. (See accompanying / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ] +[template ticket[number]''''''#[number]''''''] + [section:changes Change Log] [h2 Review Version] @@ -211,6 +214,7 @@ C++11 support has resulted in some breaking changes: * Faster assign, which assigns to existing nodes where possible, rather than creating entirely new nodes and copy constructing. +* Fixed bug in `erase_range` ([ticket 7471]). * Reverted some of the internal changes to how nodes are created, especially for C++11 compilers. 'construct' and 'destroy' should work a little better for C++11 allocators. diff --git a/include/boost/unordered/detail/equivalent.hpp b/include/boost/unordered/detail/equivalent.hpp index c112b398..3558b1cb 100644 --- a/include/boost/unordered/detail/equivalent.hpp +++ b/include/boost/unordered/detail/equivalent.hpp @@ -676,9 +676,9 @@ namespace boost { namespace unordered { namespace detail { if(begin == group2) { link_pointer end1 = group1->group_prev_; - link_pointer end2 = group2->group_prev_; + link_pointer end2 = end->group_prev_; group1->group_prev_ = end2; - group2->group_prev_ = end1; + end->group_prev_ = end1; } } } diff --git a/include/boost/unordered/detail/table.hpp b/include/boost/unordered/detail/table.hpp index a1c4dd69..af376fe7 100644 --- a/include/boost/unordered/detail/table.hpp +++ b/include/boost/unordered/detail/table.hpp @@ -618,7 +618,16 @@ namespace boost { namespace unordered { namespace detail { { for(;;) { n = static_cast(n->next_); - if (n == end) return; + if (n == end) { + if (n) { + std::size_t new_bucket_index = + policy::to_bucket(bucket_count_, n->hash_); + if (bucket_index != new_bucket_index) { + get_bucket(new_bucket_index)->next_ = prev; + } + } + return; + } std::size_t new_bucket_index = policy::to_bucket(bucket_count_, n->hash_); diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 8e2b3aa6..13462350 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -18,6 +18,7 @@ namespace test // Note that the default hash function will work for any equal_to (but not // very well). class object; + class movable; class implicitly_convertible; class hash; class less; @@ -25,6 +26,7 @@ namespace test template class allocator1; template class allocator2; object generate(object const*); + movable generate(movable const*); implicitly_convertible generate(implicitly_convertible const*); inline void ignore_variable(void const*) {} @@ -67,6 +69,81 @@ namespace test } }; + class movable : private counted_object + { + friend class hash; + friend class equal_to; + friend class less; + int tag1_, tag2_; + + BOOST_COPYABLE_AND_MOVABLE(movable) + public: + explicit movable(int t1 = 0, int t2 = 0) : tag1_(t1), tag2_(t2) {} + + movable(movable const& x) : + counted_object(x), tag1_(x.tag1_), tag2_(x.tag2_) + { + BOOST_TEST(x.tag1_ != -1); + } + + movable(BOOST_RV_REF(movable) x) : + counted_object(x), tag1_(x.tag1_), tag2_(x.tag2_) + { + BOOST_TEST(x.tag1_ != -1); + x.tag1_ = -1; + x.tag2_ = -1; + } + + movable& operator=(BOOST_COPY_ASSIGN_REF(movable) x) // Copy assignment + { + BOOST_TEST(x.tag1_ != -1); + tag1_ = x.tag1_; + tag2_ = x.tag2_; + return *this; + } + + movable& operator=(BOOST_RV_REF(movable) x) //Move assignment + { + BOOST_TEST(x.tag1_ != -1); + tag1_ = x.tag1_; + tag2_ = x.tag2_; + x.tag1_ = -1; + x.tag2_ = -1; + return *this; + } + + ~movable() { + tag1_ = -1; + tag2_ = -1; + } + + friend bool operator==(movable const& x1, movable const& x2) { + BOOST_TEST(x1.tag1_ != -1 && x2.tag1_ != -1); + return x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_; + } + + friend bool operator!=(movable const& x1, movable const& x2) { + BOOST_TEST(x1.tag1_ != -1 && x2.tag1_ != -1); + return x1.tag1_ != x2.tag1_ || x1.tag2_ != x2.tag2_; + } + + friend bool operator<(movable const& x1, movable const& x2) { + BOOST_TEST(x1.tag1_ != -1 && x2.tag1_ != -1); + return x1.tag1_ < x2.tag1_ || + (x1.tag1_ == x2.tag1_ && x1.tag2_ < x2.tag2_); + } + + friend movable generate(movable const*) { + int* x = 0; + return movable(generate(x), generate(x)); + } + + friend std::ostream& operator<<(std::ostream& out, movable const& o) + { + return out<<"("< #include #include @@ -51,12 +52,21 @@ struct collision2_hash int operator()(int x) const { return x & 1; } }; +// For testing erase in lots of buckets. +struct collision3_hash +{ + int operator()(int x) const { return x; } +}; + typedef boost::unordered_multimap, test::allocator1 > > collide_map; typedef boost::unordered_multimap, test::allocator2 > > collide_map2; +typedef boost::unordered_multimap, + test::allocator2 > > collide_map3; typedef collide_map::value_type collide_value; typedef test::list collide_list; @@ -66,6 +76,7 @@ UNORDERED_AUTO_TEST(empty_range_tests) x.erase(x.begin(), x.end()); x.erase(x.begin(), x.begin()); x.erase(x.end(), x.end()); + test::check_equivalent_keys(x); } UNORDERED_AUTO_TEST(single_item_tests) @@ -76,10 +87,13 @@ UNORDERED_AUTO_TEST(single_item_tests) collide_map x(init.begin(), init.end()); x.erase(x.begin(), x.begin()); BOOST_TEST(x.count(1) == 1 && x.size() == 1); + test::check_equivalent_keys(x); x.erase(x.end(), x.end()); BOOST_TEST(x.count(1) == 1 && x.size() == 1); + test::check_equivalent_keys(x); x.erase(x.begin(), x.end()); BOOST_TEST(x.count(1) == 0 && x.size() == 0); + test::check_equivalent_keys(x); } UNORDERED_AUTO_TEST(two_equivalent_item_tests) @@ -92,6 +106,7 @@ UNORDERED_AUTO_TEST(two_equivalent_item_tests) collide_map x(init.begin(), init.end()); x.erase(x.begin(), x.end()); BOOST_TEST(x.count(1) == 0 && x.size() == 0); + test::check_equivalent_keys(x); } { @@ -100,6 +115,7 @@ UNORDERED_AUTO_TEST(two_equivalent_item_tests) x.erase(x.begin(), boost::next(x.begin())); BOOST_TEST(x.count(1) == 1 && x.size() == 1 && x.begin()->first == 1 && x.begin()->second == value); + test::check_equivalent_keys(x); } { @@ -108,6 +124,7 @@ UNORDERED_AUTO_TEST(two_equivalent_item_tests) x.erase(boost::next(x.begin()), x.end()); BOOST_TEST(x.count(1) == 1 && x.size() == 1 && x.begin()->first == 1 && x.begin()->second == value); + test::check_equivalent_keys(x); } } @@ -129,6 +146,8 @@ bool general_erase_range_test(Container& x, std::size_t start, std::size_t end) collide_list l(x.begin(), x.end()); l.erase(boost::next(l.begin(), start), boost::next(l.begin(), end)); x.erase(boost::next(x.begin(), start), boost::next(x.begin(), end)); + + test::check_equivalent_keys(x); return compare(l, x); } @@ -191,4 +210,11 @@ UNORDERED_AUTO_TEST(exhaustive_collide2_tests) std::cout<<"\n"; } +UNORDERED_AUTO_TEST(exhaustive_collide3_tests) +{ + std::cout<<"exhaustive_collide3_tests:\n"; + exhaustive_erase_tests((collide_map3*) 0, 8, 4); + std::cout<<"\n"; +} + RUN_TESTS() diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index c634b890..4135af4d 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_tests.cpp @@ -15,6 +15,7 @@ #include "../helpers/tracker.hpp" #include "../helpers/equivalent.hpp" #include "../helpers/helpers.hpp" +#include "../helpers/invariants.hpp" #include @@ -32,6 +33,7 @@ void erase_tests1(Container*, test::random_generator generator) test::random_values v(1000, generator); Container x(v.begin(), v.end()); + int iterations = 0; for(BOOST_DEDUCED_TYPENAME test::random_values::iterator it = v.begin(); it != v.end(); ++it) { @@ -41,6 +43,7 @@ void erase_tests1(Container*, test::random_generator generator) BOOST_TEST(x.size() == old_size - count); BOOST_TEST(x.count(test::get_key(*it)) == 0); BOOST_TEST(x.find(test::get_key(*it)) == x.end()); + if (++iterations % 20 == 0) test::check_equivalent_keys(x); } } @@ -51,6 +54,7 @@ void erase_tests1(Container*, test::random_generator generator) test::random_values v(1000, generator); Container x(v.begin(), v.end()); std::size_t size = x.size(); + int iterations = 0; while(size > 0 && !x.empty()) { BOOST_DEDUCED_TYPENAME Container::key_type @@ -62,6 +66,7 @@ void erase_tests1(Container*, test::random_generator generator) BOOST_TEST(pos == x.begin()); BOOST_TEST(x.count(key) == count - 1); BOOST_TEST(x.size() == size); + if (++iterations % 20 == 0) test::check_equivalent_keys(x); } BOOST_TEST(x.empty()); } @@ -73,6 +78,7 @@ void erase_tests1(Container*, test::random_generator generator) test::random_values v(1000, generator); Container x(v.begin(), v.end()); std::size_t size = x.size(); + int iterations = 0; while(size > 0 && !x.empty()) { using namespace std; @@ -96,6 +102,7 @@ void erase_tests1(Container*, test::random_generator generator) next == boost::next(prev)); BOOST_TEST(x.count(key) == count - 1); BOOST_TEST(x.size() == size); + if (++iterations % 20 == 0) test::check_equivalent_keys(x); } BOOST_TEST(x.empty()); } @@ -116,12 +123,15 @@ void erase_tests1(Container*, test::random_generator generator) BOOST_TEST(x.erase(x.end(), x.end()) == x.end()); BOOST_TEST(x.erase(x.begin(), x.begin()) == x.begin()); BOOST_TEST(x.size() == size); + test::check_equivalent_keys(x); BOOST_TEST(x.erase(x.begin(), x.end()) == x.end()); BOOST_TEST(x.empty()); BOOST_TEST(x.begin() == x.end()); + test::check_equivalent_keys(x); BOOST_TEST(x.erase(x.begin(), x.end()) == x.begin()); + test::check_equivalent_keys(x); } std::cerr<<"quick_erase(begin()).\n"; @@ -131,6 +141,7 @@ void erase_tests1(Container*, test::random_generator generator) test::random_values v(1000, generator); Container x(v.begin(), v.end()); std::size_t size = x.size(); + int iterations = 0; while(size > 0 && !x.empty()) { BOOST_DEDUCED_TYPENAME Container::key_type @@ -140,6 +151,7 @@ void erase_tests1(Container*, test::random_generator generator) --size; BOOST_TEST(x.count(key) == count - 1); BOOST_TEST(x.size() == size); + if (++iterations % 20 == 0) test::check_equivalent_keys(x); } BOOST_TEST(x.empty()); } @@ -151,6 +163,7 @@ void erase_tests1(Container*, test::random_generator generator) test::random_values v(1000, generator); Container x(v.begin(), v.end()); std::size_t size = x.size(); + int iterations = 0; while(size > 0 && !x.empty()) { using namespace std; @@ -174,6 +187,7 @@ void erase_tests1(Container*, test::random_generator generator) next == boost::next(prev)); BOOST_TEST(x.count(key) == count - 1); BOOST_TEST(x.size() == size); + if (++iterations % 20 == 0) test::check_equivalent_keys(x); } BOOST_TEST(x.empty()); } diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index a136b9c1..7208f54b 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -16,6 +16,7 @@ #include "../helpers/equivalent.hpp" #include "../helpers/invariants.hpp" #include "../helpers/input_iterator.hpp" +#include "../helpers/helpers.hpp" #include @@ -293,8 +294,6 @@ void insert_tests2(X*, test::random_generator generator) } } -#if !defined(BOOST_NO_RVALUE_REFERENCES) && !defined(BOOST_NO_VARIADIC_TEMPLATES) - template void unique_emplace_tests1(X*, test::random_generator generator) { @@ -361,7 +360,73 @@ void equivalent_emplace_tests1(X*, test::random_generator generator) test::check_equivalent_keys(x); } -#endif +template +void move_emplace_tests(X*, test::random_generator generator) +{ + typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + typedef test::ordered ordered; + + std::cerr<<"emplace(move(value)) tests for containers with unique keys.\n"; + + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + + for(BOOST_DEDUCED_TYPENAME test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + typename X::value_type value = *it; + x.emplace(boost::move(value)); + tracker.insert(*it); + tracker.compare_key(x, *it); + + if(static_cast(x.size()) < b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + tracker.compare(x); +} + +template +void default_emplace_tests(X*, test::random_generator) +{ + std::cerr<<"emplace() tests.\n"; + bool is_unique = test::has_unique_keys::value; + + X x; + + x.emplace(); + BOOST_TEST(x.size() == 1); + x.emplace(); + BOOST_TEST(x.size() == is_unique ? 1: 2); + x.emplace(); + BOOST_TEST(x.size() == is_unique ? 1: 3); + + typename X::value_type y; + BOOST_TEST(x.count(test::get_key(y)) == is_unique ? 1: 3); + BOOST_TEST(*x.equal_range(test::get_key(y)).first == y); + + x.emplace(y); + BOOST_TEST(x.size() == is_unique ? 1: 4); + BOOST_TEST(x.count(test::get_key(y)) == is_unique ? 1: 4); + BOOST_TEST(*x.equal_range(test::get_key(y)).first == y); + + x.clear(); + BOOST_TEST(x.empty()); + x.emplace(y); + BOOST_TEST(x.size() == 1); + x.emplace(y); + BOOST_TEST(x.size() == is_unique ? 1: 2); + + BOOST_TEST(x.count(test::get_key(y)) == is_unique ? 1: 2); + BOOST_TEST(*x.equal_range(test::get_key(y)).first == y); +} template void map_tests(X*, test::random_generator generator) @@ -434,9 +499,9 @@ void map_insert_range_test2(X*, test::random_generator generator) test::check_equivalent_keys(x); } -boost::unordered_set >* test_set_std_alloc; + std::allocator >* test_set_std_alloc; boost::unordered_multimap >* test_multimap_std_alloc; @@ -444,12 +509,12 @@ boost::unordered_multimap >* test_set; -boost::unordered_multiset >* test_multiset; -boost::unordered_map >* test_multiset; +boost::unordered_map >* test_map; + test::allocator2 >* test_map; boost::unordered_multimap >* test_multimap; @@ -472,7 +537,6 @@ UNORDERED_TEST(insert_tests2, ((default_generator)(generate_collisions)) ) -#if !defined(BOOST_NO_RVALUE_REFERENCES) && !defined(BOOST_NO_VARIADIC_TEMPLATES) UNORDERED_TEST(unique_emplace_tests1, ((test_set_std_alloc)(test_set)(test_map)) ((default_generator)(generate_collisions)) @@ -482,7 +546,18 @@ UNORDERED_TEST(equivalent_emplace_tests1, ((test_multimap_std_alloc)(test_multiset)(test_multimap)) ((default_generator)(generate_collisions)) ) -#endif + +UNORDERED_TEST(move_emplace_tests, + ((test_set_std_alloc)(test_multimap_std_alloc)(test_set)(test_map) + (test_multiset)(test_multimap)) + ((default_generator)(generate_collisions)) +) + +UNORDERED_TEST(default_emplace_tests, + ((test_set_std_alloc)(test_multimap_std_alloc)(test_set)(test_map) + (test_multiset)(test_multimap)) + ((default_generator)(generate_collisions)) +) UNORDERED_TEST(map_tests, ((test_map)) diff --git a/test/unordered/rehash_tests.cpp b/test/unordered/rehash_tests.cpp index c77115b5..3746bed3 100644 --- a/test/unordered/rehash_tests.cpp +++ b/test/unordered/rehash_tests.cpp @@ -12,6 +12,7 @@ #include "../helpers/random_values.hpp" #include "../helpers/tracker.hpp" #include "../helpers/metafunctions.hpp" +#include "../objects/test.hpp" namespace rehash_tests { @@ -36,6 +37,9 @@ void rehash_empty_test1(X*) x.rehash(0); BOOST_TEST(postcondition(x, 0)); + + x.rehash(10000000); + BOOST_TEST(postcondition(x, 10000000)); } template @@ -54,6 +58,10 @@ void rehash_empty_test2(X*, test::random_generator generator) tracker.compare(x); BOOST_TEST(postcondition(x, 10000)); + + x.rehash(10000000); + tracker.compare(x); + BOOST_TEST(postcondition(x, 10000000)); } template @@ -74,7 +82,6 @@ void rehash_empty_test3(X*, test::random_generator generator) BOOST_TEST(postcondition(x, 0)); } - template void rehash_test1(X*, test::random_generator generator) { @@ -98,6 +105,35 @@ void rehash_test1(X*, test::random_generator generator) tracker.compare(x); } +template +void reserve_empty_test1(X*) +{ + X x; + + x.reserve(10000); + BOOST_TEST(x.bucket_count() >= 10000); + + x.reserve(0); + + x.reserve(10000000); + BOOST_TEST(x.bucket_count() >= 10000000); +} + +template +void reserve_empty_test2(X*) +{ + X x; + x.max_load_factor(0.25); + + x.reserve(10000); + BOOST_TEST(x.bucket_count() >= 40000); + + x.reserve(0); + + x.reserve(10000000); + BOOST_TEST(x.bucket_count() >= 40000000); +} + template void reserve_test1(X*, test::random_generator generator) { @@ -165,34 +201,44 @@ void reserve_test2(X*, test::random_generator generator) } boost::unordered_set* int_set_ptr; -boost::unordered_multiset* int_multiset_ptr; -boost::unordered_map* int_map_ptr; +boost::unordered_multiset >* test_multiset_ptr; +boost::unordered_map >* test_map_ptr; boost::unordered_multimap* int_multimap_ptr; using test::default_generator; using test::generate_collisions; UNORDERED_TEST(rehash_empty_test1, - ((int_set_ptr)(int_multiset_ptr)(int_map_ptr)(int_multimap_ptr)) + ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr)) ) UNORDERED_TEST(rehash_empty_test2, - ((int_set_ptr)(int_multiset_ptr)(int_map_ptr)(int_multimap_ptr)) + ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr)) ((default_generator)(generate_collisions)) ) UNORDERED_TEST(rehash_empty_test3, - ((int_set_ptr)(int_multiset_ptr)(int_map_ptr)(int_multimap_ptr)) + ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr)) ((default_generator)(generate_collisions)) ) UNORDERED_TEST(rehash_test1, - ((int_set_ptr)(int_multiset_ptr)(int_map_ptr)(int_multimap_ptr)) + ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr)) ((default_generator)(generate_collisions)) ) +UNORDERED_TEST(reserve_empty_test1, + ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr)) +) +UNORDERED_TEST(reserve_empty_test2, + ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr)) +) UNORDERED_TEST(reserve_test1, - ((int_set_ptr)(int_multiset_ptr)(int_map_ptr)(int_multimap_ptr)) + ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr)) ((default_generator)(generate_collisions)) ) UNORDERED_TEST(reserve_test2, - ((int_set_ptr)(int_multiset_ptr)(int_map_ptr)(int_multimap_ptr)) + ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr)) ((default_generator)(generate_collisions)) ) diff --git a/test/unordered/unnecessary_copy_tests.cpp b/test/unordered/unnecessary_copy_tests.cpp index 284dd56b..143262aa 100644 --- a/test/unordered/unnecessary_copy_tests.cpp +++ b/test/unordered/unnecessary_copy_tests.cpp @@ -20,8 +20,17 @@ namespace unnecessary_copy_tests public: static int copies; static int moves; - count_copies() : tag_(0) { ++copies; } - explicit count_copies(int tag) : tag_(tag) { ++copies; } + static int id_count; + + count_copies() : tag_(0), id_(++id_count) { + ++copies; + trace_op("Default construct"); + } + + explicit count_copies(int tag) : tag_(tag), id_(++id_count) { + ++copies; + trace_op("Tag construct"); + } // This bizarre constructor is an attempt to confuse emplace. // @@ -33,17 +42,30 @@ namespace unnecessary_copy_tests // The second emplace should use the single argument contructor for // the key, and this constructor for the value. count_copies(count_copies const&, count_copies const& x) - : tag_(x.tag_) { ++copies; } + : tag_(x.tag_), id_(++id_count) + { + ++copies; + trace_op("Pair construct"); + } - count_copies(count_copies const& x) : tag_(x.tag_) { ++copies; } - count_copies(BOOST_RV_REF(count_copies) x) : tag_(x.tag_) { + count_copies(count_copies const& x) : tag_(x.tag_), id_(++id_count) + { + ++copies; + trace_op("Copy construct"); + } + + count_copies(BOOST_RV_REF(count_copies) x) : + tag_(x.tag_), id_(++id_count) + { x.tag_ = -1; ++moves; + trace_op("Move construct"); } count_copies& operator=(BOOST_COPY_ASSIGN_REF(count_copies) p) // Copy assignment { tag_ = p.tag_; ++copies; + trace_op("Copy assign"); return *this; } @@ -51,10 +73,21 @@ namespace unnecessary_copy_tests { tag_ = p.tag_; ++moves; + trace_op("Move assign"); return *this; } + ~count_copies() { + trace_op("Destruct"); + } + + void trace_op(char const* str) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << str << ": " << tag_ + << " (#" << id_ << ")" < void unnecessary_copy_insert_test(T*) @@ -337,7 +374,16 @@ namespace unnecessary_copy_tests // COPY_COUNT(1) would be okay here. reset(); x.emplace(); +# if BOOST_WORKAROUND(BOOST_MSVC, >= 1700) + // This is a little odd, Visual C++ 11 seems to move the pair, which + // results in one copy (for the const key) and one move (for the + // non-const mapped value). Since 'emplace(boost::move(a))' (see below) + // has the normal result, it must be some odd consequence of how + // Visual C++ 11 handles calling move for default arguments. + COPY_COUNT(3); MOVE_COUNT(1); +# else COPY_COUNT(2); MOVE_COUNT(0); +# endif #endif reset();