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();