2 // Copyright 2006-2009 Daniel James.
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 #include "../helpers/prefix.hpp"
8 #include <boost/unordered_set.hpp>
9 #include <boost/unordered_map.hpp>
10 #include "../helpers/postfix.hpp"
14 #include "../helpers/test.hpp"
16 namespace unnecessary_copy_tests {
20 BOOST_COPYABLE_AND_MOVABLE(count_copies)
26 count_copies() : tag_(0), id_(++id_count)
29 trace_op("Default construct");
32 explicit count_copies(int tag) : tag_(tag), id_(++id_count)
35 trace_op("Tag construct");
38 // This bizarre constructor is an attempt to confuse emplace.
40 // unordered_map<count_copies, count_copies> x:
41 // x.emplace(count_copies(1), count_copies(2));
42 // x.emplace(count_copies(1), count_copies(2), count_copies(3));
44 // The first emplace should use the single argument constructor twice.
45 // The second emplace should use the single argument contructor for
46 // the key, and this constructor for the value.
47 count_copies(count_copies const&, count_copies const& x)
48 : tag_(x.tag_), id_(++id_count)
51 trace_op("Pair construct");
54 count_copies(count_copies const& x) : tag_(x.tag_), id_(++id_count)
57 trace_op("Copy construct");
60 count_copies(BOOST_RV_REF(count_copies) x) : tag_(x.tag_), id_(++id_count)
64 trace_op("Move construct");
67 count_copies& operator=(
68 BOOST_COPY_ASSIGN_REF(count_copies) p) // Copy assignment
72 trace_op("Copy assign");
76 count_copies& operator=(BOOST_RV_REF(count_copies) p) // Move assignment
80 trace_op("Move assign");
84 ~count_copies() { trace_op("Destruct"); }
86 void trace_op(char const* str)
88 BOOST_LIGHTWEIGHT_TEST_OSTREAM << str << ": " << tag_ << " (#" << id_
96 bool operator==(count_copies const& x, count_copies const& y)
98 return x.tag_ == y.tag_;
101 template <class T> T source() { return T(); }
105 count_copies::copies = 0;
106 count_copies::moves = 0;
108 BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\nReset\n" << std::endl;
112 #if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
115 namespace unnecessary_copy_tests
118 std::size_t hash_value(unnecessary_copy_tests::count_copies const& x)
120 return static_cast<std::size_t>(x.tag_);
124 // Boost.Move doesn't seem to work very well on this compiler.
129 // It will default construct T, and then move it in.
130 // For 'T const' it seems to copy.
132 #if defined(__IBMCPP__) && __IBMCPP__ <= 1210
133 #define EXTRA_CONSTRUCT_COST 1
135 #define EXTRA_CONSTRUCT_COST 0
138 #define COPY_COUNT(n) \
139 if (::unnecessary_copy_tests::count_copies::copies != n) { \
140 BOOST_ERROR("Wrong number of copies."); \
141 std::cerr << "Number of copies: " \
142 << ::unnecessary_copy_tests::count_copies::copies \
143 << " expecting: " << n << std::endl; \
145 #define MOVE_COUNT(n) \
146 if (::unnecessary_copy_tests::count_copies::moves != n) { \
147 BOOST_ERROR("Wrong number of moves."); \
148 std::cerr << "Number of moves: " \
149 << ::unnecessary_copy_tests::count_copies::moves \
150 << " expecting: " << n << std::endl; \
152 #define COPY_COUNT_RANGE(a, b) \
153 if (::unnecessary_copy_tests::count_copies::copies < a || \
154 ::unnecessary_copy_tests::count_copies::copies > b) { \
155 BOOST_ERROR("Wrong number of copies."); \
156 std::cerr << "Number of copies: " \
157 << ::unnecessary_copy_tests::count_copies::copies \
158 << " expecting: [" << a << ", " << b << "]" << std::endl; \
160 #define MOVE_COUNT_RANGE(a, b) \
161 if (::unnecessary_copy_tests::count_copies::moves < a || \
162 ::unnecessary_copy_tests::count_copies::moves > b) { \
163 BOOST_ERROR("Wrong number of moves."); \
164 std::cerr << "Number of moves: " \
165 << ::unnecessary_copy_tests::count_copies::moves \
166 << " expecting: [" << a << ", " << b << "]" << std::endl; \
168 #define COPY_COUNT_EXTRA(a, b) COPY_COUNT_RANGE(a, a + b * EXTRA_CONSTRUCT_COST)
169 #define MOVE_COUNT_EXTRA(a, b) MOVE_COUNT_RANGE(a, a + b * EXTRA_CONSTRUCT_COST)
171 namespace unnecessary_copy_tests {
172 int count_copies::copies;
173 int count_copies::moves;
174 int count_copies::id_count;
176 template <class T> void unnecessary_copy_insert_test(T*)
179 BOOST_DEDUCED_TYPENAME T::value_type a;
185 boost::unordered_set<count_copies>* set;
186 boost::unordered_multiset<count_copies>* multiset;
187 boost::unordered_map<int, count_copies>* map;
188 boost::unordered_multimap<int, count_copies>* multimap;
190 UNORDERED_TEST(unnecessary_copy_insert_test, ((set)(multiset)(map)(multimap)))
192 template <class T> void unnecessary_copy_emplace_test(T*)
196 BOOST_DEDUCED_TYPENAME T::value_type a;
202 template <class T> void unnecessary_copy_emplace_rvalue_test(T*)
206 x.emplace(source<BOOST_DEDUCED_TYPENAME T::value_type>());
207 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
214 UNORDERED_TEST(unnecessary_copy_emplace_test, ((set)(multiset)(map)(multimap)))
216 unnecessary_copy_emplace_rvalue_test, ((set)(multiset)(map)(multimap)))
218 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
219 template <class T> void unnecessary_copy_emplace_std_move_test(T*)
223 BOOST_DEDUCED_TYPENAME T::value_type a;
226 x.emplace(std::move(a));
232 unnecessary_copy_emplace_std_move_test, ((set)(multiset)(map)(multimap)))
235 template <class T> void unnecessary_copy_emplace_boost_move_test(T*)
239 BOOST_DEDUCED_TYPENAME T::value_type a;
241 MOVE_COUNT_EXTRA(0, 1);
242 x.emplace(boost::move(a));
243 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
247 // Since std::pair isn't movable, move only works for sets.
248 COPY_COUNT_RANGE(1, 2);
249 MOVE_COUNT_RANGE(0, 1);
254 unnecessary_copy_emplace_boost_move_test, ((set)(multiset)(map)(multimap)))
256 template <class T> void unnecessary_copy_emplace_boost_move_set_test(T*)
260 BOOST_DEDUCED_TYPENAME T::value_type a;
263 x.emplace(boost::move(a));
268 UNORDERED_TEST(unnecessary_copy_emplace_boost_move_set_test, ((set)(multiset)))
270 template <class T> void unnecessary_copy_emplace_boost_move_map_test(T*)
276 BOOST_DEDUCED_TYPENAME T::value_type a;
278 MOVE_COUNT_EXTRA(0, 1);
279 x.emplace(boost::move(a));
280 #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
282 MOVE_COUNT_EXTRA(0, 1);
289 UNORDERED_TEST(unnecessary_copy_emplace_boost_move_map_test, ((map)(multimap)))
291 UNORDERED_AUTO_TEST(unnecessary_copy_emplace_set_test)
293 // When calling 'source' the object is moved on some compilers, but not
294 // others. So count that here to adjust later.
297 source<count_copies>();
298 int source_cost = ::unnecessary_copy_tests::count_copies::moves;
303 boost::unordered_set<count_copies> x;
313 #if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100))
314 // The container will have to create a copy in order to compare with
315 // the existing element.
318 #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || \
319 !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
320 // source_cost doesn't make much sense here, but it seems to fit.
322 MOVE_COUNT(source_cost);
325 MOVE_COUNT(1 + source_cost);
333 // Emplace should be able to tell that there already is an element
334 // without creating a new one.
340 // A new object is created by source, but it shouldn't be moved or
343 x.emplace(source<count_copies>());
345 MOVE_COUNT(source_cost);
347 // No move should take place.
349 x.emplace(boost::move(a));
350 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
358 // Use a new value for cases where a did get moved...
361 // The container will have to create a copy in order to compare with
362 // the existing element.
372 // The container will have to create b copy in order to compare with
373 // the existing element.
375 // Note to self: If copy_count == 0 it's an error not an optimization.
376 // TODO: Devise a better test.
385 UNORDERED_AUTO_TEST(unnecessary_copy_emplace_map_test)
387 // When calling 'source' the object is moved on some compilers, but not
388 // others. So count that here to adjust later.
391 source<count_copies>();
392 int source_cost = ::unnecessary_copy_tests::count_copies::moves;
395 source<std::pair<count_copies, count_copies> >();
396 int source_pair_cost = ::unnecessary_copy_tests::count_copies::moves;
401 boost::unordered_map<count_copies, count_copies> x;
402 // TODO: Run tests for pairs without const etc.
403 std::pair<count_copies const, count_copies> a;
405 COPY_COUNT_EXTRA(4, 1);
406 MOVE_COUNT_EXTRA(0, 1);
412 #if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100))
413 // COPY_COUNT(1) would be okay here.
416 #if BOOST_WORKAROUND(BOOST_MSVC, == 1700)
417 // This is a little odd, Visual C++ 11 seems to move the pair, which
418 // results in one copy (for the const key) and one move (for the
419 // non-const mapped value). Since 'emplace(boost::move(a))' (see below)
420 // has the normal result, it must be some odd consequence of how
421 // Visual C++ 11 handles calling move for default arguments.
425 COPY_COUNT_EXTRA(2, 1);
426 MOVE_COUNT_EXTRA(0, 1);
431 x.emplace(boost::unordered::piecewise_construct, boost::make_tuple(),
432 boost::make_tuple());
445 // A new object is created by source, but it shouldn't be moved or
448 x.emplace(source<std::pair<count_copies, count_copies> >());
450 MOVE_COUNT(source_pair_cost);
452 #if !(defined(__GNUC__) && __cplusplus < 199900L) && \
453 !(defined(_MSC_VER) && _MSC_VER < 1600)
456 std::pair<count_copies const&, count_copies const&> a_ref(part, part);
463 // No move should take place.
464 // (since a is already in the container)
466 x.emplace(boost::move(a));
474 std::pair<count_copies const, count_copies> b;
477 x.emplace(b.first, b.second);
482 x.emplace(source<count_copies>(), source<count_copies>());
484 MOVE_COUNT(source_cost * 2);
486 // source<count_copies> creates a single copy.
488 x.emplace(b.first, source<count_copies>());
490 MOVE_COUNT(source_cost);
493 x.emplace(count_copies(b.first.tag_), count_copies(b.second.tag_));
498 x.emplace(boost::unordered::piecewise_construct,
499 boost::make_tuple(boost::ref(b.first)),
500 boost::make_tuple(boost::ref(b.second)));
504 #if !defined(BOOST_NO_CXX11_HDR_TUPLE) || defined(BOOST_HAS_TR1_TUPLE)
507 x.emplace(boost::unordered::piecewise_construct,
508 std::make_tuple(std::ref(b.first)),
509 std::make_tuple(std::ref(b.second)));
513 std::pair<count_copies const, count_copies> move_source_trial;
515 std::make_tuple(std::move(move_source_trial.first));
516 std::make_tuple(std::move(move_source_trial.second));
517 int tuple_move_cost = ::unnecessary_copy_tests::count_copies::moves;
518 int tuple_copy_cost = ::unnecessary_copy_tests::count_copies::copies;
520 std::pair<count_copies const, count_copies> move_source;
522 x.emplace(boost::unordered::piecewise_construct,
523 std::make_tuple(std::move(move_source.first)),
524 std::make_tuple(std::move(move_source.second)));
525 COPY_COUNT(tuple_copy_cost);
526 MOVE_COUNT(tuple_move_cost);
528 #if !defined(BOOST_NO_CXX11_HDR_TUPLE) && \
529 !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6) && \
530 !(defined(BOOST_MSVC) && BOOST_MSVC < 1700)
532 x.emplace(boost::unordered::piecewise_construct,
533 std::forward_as_tuple(b.first), std::forward_as_tuple(b.second));