forked from boostorg/unordered
		
	
		
			
				
	
	
		
			543 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			543 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
// Copyright 2006-2009 Daniel James.
 | 
						|
// 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)
 | 
						|
 | 
						|
// clang-format off
 | 
						|
#include "../helpers/prefix.hpp"
 | 
						|
#include <boost/unordered_set.hpp>
 | 
						|
#include <boost/unordered_map.hpp>
 | 
						|
#include "../helpers/postfix.hpp"
 | 
						|
// clang-format on
 | 
						|
 | 
						|
#include <iostream>
 | 
						|
#include "../helpers/test.hpp"
 | 
						|
 | 
						|
namespace unnecessary_copy_tests {
 | 
						|
struct count_copies
 | 
						|
{
 | 
						|
  private:
 | 
						|
    BOOST_COPYABLE_AND_MOVABLE(count_copies)
 | 
						|
  public:
 | 
						|
    static int copies;
 | 
						|
    static int moves;
 | 
						|
    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.
 | 
						|
    //
 | 
						|
    // unordered_map<count_copies, count_copies> x:
 | 
						|
    // x.emplace(count_copies(1), count_copies(2));
 | 
						|
    // x.emplace(count_copies(1), count_copies(2), count_copies(3));
 | 
						|
    //
 | 
						|
    // The first emplace should use the single argument constructor twice.
 | 
						|
    // 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_), id_(++id_count)
 | 
						|
    {
 | 
						|
        ++copies;
 | 
						|
        trace_op("Pair construct");
 | 
						|
    }
 | 
						|
 | 
						|
    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;
 | 
						|
    }
 | 
						|
 | 
						|
    count_copies& operator=(BOOST_RV_REF(count_copies) p) // Move assignment
 | 
						|
    {
 | 
						|
        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_
 | 
						|
                                       << ")" << std::endl;
 | 
						|
    }
 | 
						|
 | 
						|
    int tag_;
 | 
						|
    int id_;
 | 
						|
};
 | 
						|
 | 
						|
bool operator==(count_copies const& x, count_copies const& y)
 | 
						|
{
 | 
						|
    return x.tag_ == y.tag_;
 | 
						|
}
 | 
						|
 | 
						|
template <class T> T source() { return T(); }
 | 
						|
 | 
						|
void reset()
 | 
						|
{
 | 
						|
    count_copies::copies = 0;
 | 
						|
    count_copies::moves = 0;
 | 
						|
 | 
						|
    BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\nReset\n" << std::endl;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
 | 
						|
namespace boost
 | 
						|
#else
 | 
						|
namespace unnecessary_copy_tests
 | 
						|
#endif
 | 
						|
{
 | 
						|
std::size_t hash_value(unnecessary_copy_tests::count_copies const& x)
 | 
						|
{
 | 
						|
    return static_cast<std::size_t>(x.tag_);
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
// Boost.Move doesn't seem to work very well on this compiler.
 | 
						|
// For example for:
 | 
						|
//
 | 
						|
//     T x;
 | 
						|
//
 | 
						|
// It will default construct T, and then move it in.
 | 
						|
// For 'T const' it seems to copy.
 | 
						|
 | 
						|
#if defined(__IBMCPP__) && __IBMCPP__ <= 1210
 | 
						|
#define EXTRA_CONSTRUCT_COST 1
 | 
						|
#else
 | 
						|
#define EXTRA_CONSTRUCT_COST 0
 | 
						|
#endif
 | 
						|
 | 
						|
#define COPY_COUNT(n)                                                          \
 | 
						|
    if (::unnecessary_copy_tests::count_copies::copies != n) {                 \
 | 
						|
        BOOST_ERROR("Wrong number of copies.");                                \
 | 
						|
        std::cerr << "Number of copies: "                                      \
 | 
						|
                  << ::unnecessary_copy_tests::count_copies::copies            \
 | 
						|
                  << " expecting: " << n << std::endl;                         \
 | 
						|
    }
 | 
						|
#define MOVE_COUNT(n)                                                          \
 | 
						|
    if (::unnecessary_copy_tests::count_copies::moves != n) {                  \
 | 
						|
        BOOST_ERROR("Wrong number of moves.");                                 \
 | 
						|
        std::cerr << "Number of moves: "                                       \
 | 
						|
                  << ::unnecessary_copy_tests::count_copies::moves             \
 | 
						|
                  << " expecting: " << n << std::endl;                         \
 | 
						|
    }
 | 
						|
#define COPY_COUNT_RANGE(a, b)                                                 \
 | 
						|
    if (::unnecessary_copy_tests::count_copies::copies < a ||                  \
 | 
						|
        ::unnecessary_copy_tests::count_copies::copies > b) {                  \
 | 
						|
        BOOST_ERROR("Wrong number of copies.");                                \
 | 
						|
        std::cerr << "Number of copies: "                                      \
 | 
						|
                  << ::unnecessary_copy_tests::count_copies::copies            \
 | 
						|
                  << " expecting: [" << a << ", " << b << "]" << std::endl;    \
 | 
						|
    }
 | 
						|
#define MOVE_COUNT_RANGE(a, b)                                                 \
 | 
						|
    if (::unnecessary_copy_tests::count_copies::moves < a ||                   \
 | 
						|
        ::unnecessary_copy_tests::count_copies::moves > b) {                   \
 | 
						|
        BOOST_ERROR("Wrong number of moves.");                                 \
 | 
						|
        std::cerr << "Number of moves: "                                       \
 | 
						|
                  << ::unnecessary_copy_tests::count_copies::moves             \
 | 
						|
                  << " expecting: [" << a << ", " << b << "]" << std::endl;    \
 | 
						|
    }
 | 
						|
#define COPY_COUNT_EXTRA(a, b) COPY_COUNT_RANGE(a, a + b * EXTRA_CONSTRUCT_COST)
 | 
						|
#define MOVE_COUNT_EXTRA(a, b) MOVE_COUNT_RANGE(a, a + b * EXTRA_CONSTRUCT_COST)
 | 
						|
 | 
						|
namespace unnecessary_copy_tests {
 | 
						|
int count_copies::copies;
 | 
						|
int count_copies::moves;
 | 
						|
int count_copies::id_count;
 | 
						|
 | 
						|
template <class T> void unnecessary_copy_insert_test(T*)
 | 
						|
{
 | 
						|
    T x;
 | 
						|
    BOOST_DEDUCED_TYPENAME T::value_type a;
 | 
						|
    reset();
 | 
						|
    x.insert(a);
 | 
						|
    COPY_COUNT(1);
 | 
						|
}
 | 
						|
 | 
						|
boost::unordered_set<count_copies>* set;
 | 
						|
boost::unordered_multiset<count_copies>* multiset;
 | 
						|
boost::unordered_map<int, count_copies>* map;
 | 
						|
boost::unordered_multimap<int, count_copies>* multimap;
 | 
						|
 | 
						|
UNORDERED_TEST(unnecessary_copy_insert_test, ((set)(multiset)(map)(multimap)))
 | 
						|
 | 
						|
template <class T> void unnecessary_copy_emplace_test(T*)
 | 
						|
{
 | 
						|
    reset();
 | 
						|
    T x;
 | 
						|
    BOOST_DEDUCED_TYPENAME T::value_type a;
 | 
						|
    COPY_COUNT(1);
 | 
						|
    x.emplace(a);
 | 
						|
    COPY_COUNT(2);
 | 
						|
}
 | 
						|
 | 
						|
template <class T> void unnecessary_copy_emplace_rvalue_test(T*)
 | 
						|
{
 | 
						|
    reset();
 | 
						|
    T x;
 | 
						|
    x.emplace(source<BOOST_DEDUCED_TYPENAME T::value_type>());
 | 
						|
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
 | 
						|
    COPY_COUNT(1);
 | 
						|
#else
 | 
						|
    COPY_COUNT(2);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
UNORDERED_TEST(unnecessary_copy_emplace_test, ((set)(multiset)(map)(multimap)))
 | 
						|
UNORDERED_TEST(
 | 
						|
    unnecessary_copy_emplace_rvalue_test, ((set)(multiset)(map)(multimap)))
 | 
						|
 | 
						|
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
 | 
						|
template <class T> void unnecessary_copy_emplace_std_move_test(T*)
 | 
						|
{
 | 
						|
    reset();
 | 
						|
    T x;
 | 
						|
    BOOST_DEDUCED_TYPENAME T::value_type a;
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
    x.emplace(std::move(a));
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(1);
 | 
						|
}
 | 
						|
 | 
						|
UNORDERED_TEST(
 | 
						|
    unnecessary_copy_emplace_std_move_test, ((set)(multiset)(map)(multimap)))
 | 
						|
#endif
 | 
						|
 | 
						|
template <class T> void unnecessary_copy_emplace_boost_move_test(T*)
 | 
						|
{
 | 
						|
    reset();
 | 
						|
    T x;
 | 
						|
    BOOST_DEDUCED_TYPENAME T::value_type a;
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT_EXTRA(0, 1);
 | 
						|
    x.emplace(boost::move(a));
 | 
						|
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(1);
 | 
						|
#else
 | 
						|
    // Since std::pair isn't movable, move only works for sets.
 | 
						|
    COPY_COUNT_RANGE(1, 2);
 | 
						|
    MOVE_COUNT_RANGE(0, 1);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
UNORDERED_TEST(
 | 
						|
    unnecessary_copy_emplace_boost_move_test, ((set)(multiset)(map)(multimap)))
 | 
						|
 | 
						|
template <class T> void unnecessary_copy_emplace_boost_move_set_test(T*)
 | 
						|
{
 | 
						|
    reset();
 | 
						|
    T x;
 | 
						|
    BOOST_DEDUCED_TYPENAME T::value_type a;
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
    x.emplace(boost::move(a));
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(1);
 | 
						|
}
 | 
						|
 | 
						|
UNORDERED_TEST(unnecessary_copy_emplace_boost_move_set_test, ((set)(multiset)))
 | 
						|
 | 
						|
template <class T> void unnecessary_copy_emplace_boost_move_map_test(T*)
 | 
						|
{
 | 
						|
    reset();
 | 
						|
    T x;
 | 
						|
    COPY_COUNT(0);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
    BOOST_DEDUCED_TYPENAME T::value_type a;
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT_EXTRA(0, 1);
 | 
						|
    x.emplace(boost::move(a));
 | 
						|
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
 | 
						|
    COPY_COUNT(2);
 | 
						|
    MOVE_COUNT_EXTRA(0, 1);
 | 
						|
#else
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(1);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
UNORDERED_TEST(unnecessary_copy_emplace_boost_move_map_test, ((map)(multimap)))
 | 
						|
 | 
						|
UNORDERED_AUTO_TEST(unnecessary_copy_emplace_set_test)
 | 
						|
{
 | 
						|
    // When calling 'source' the object is moved on some compilers, but not
 | 
						|
    // others. So count that here to adjust later.
 | 
						|
 | 
						|
    reset();
 | 
						|
    source<count_copies>();
 | 
						|
    int source_cost = ::unnecessary_copy_tests::count_copies::moves;
 | 
						|
 | 
						|
    //
 | 
						|
 | 
						|
    reset();
 | 
						|
    boost::unordered_set<count_copies> x;
 | 
						|
    count_copies a;
 | 
						|
    x.insert(a);
 | 
						|
    COPY_COUNT(2);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
//
 | 
						|
// 0 arguments
 | 
						|
//
 | 
						|
 | 
						|
#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100))
 | 
						|
    // The container will have to create a copy in order to compare with
 | 
						|
    // the existing element.
 | 
						|
    reset();
 | 
						|
    x.emplace();
 | 
						|
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) ||                             \
 | 
						|
    !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
 | 
						|
    // source_cost doesn't make much sense here, but it seems to fit.
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(source_cost);
 | 
						|
#else
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(1 + source_cost);
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
    //
 | 
						|
    // 1 argument
 | 
						|
    //
 | 
						|
 | 
						|
    // Emplace should be able to tell that there already is an element
 | 
						|
    // without creating a new one.
 | 
						|
    reset();
 | 
						|
    x.emplace(a);
 | 
						|
    COPY_COUNT(0);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
    // A new object is created by source, but it shouldn't be moved or
 | 
						|
    // copied.
 | 
						|
    reset();
 | 
						|
    x.emplace(source<count_copies>());
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(source_cost);
 | 
						|
 | 
						|
    // No move should take place.
 | 
						|
    reset();
 | 
						|
    x.emplace(boost::move(a));
 | 
						|
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
 | 
						|
    COPY_COUNT(0);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
#else
 | 
						|
    COPY_COUNT(0);
 | 
						|
    MOVE_COUNT(1);
 | 
						|
#endif
 | 
						|
 | 
						|
    // Use a new value for cases where a did get moved...
 | 
						|
    count_copies b;
 | 
						|
 | 
						|
    // The container will have to create a copy in order to compare with
 | 
						|
    // the existing element.
 | 
						|
    reset();
 | 
						|
    x.emplace(b.tag_);
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
    //
 | 
						|
    // 2 arguments
 | 
						|
    //
 | 
						|
 | 
						|
    // The container will have to create b copy in order to compare with
 | 
						|
    // the existing element.
 | 
						|
    //
 | 
						|
    // Note to self: If copy_count == 0 it's an error not an optimization.
 | 
						|
    // TODO: Devise a better test.
 | 
						|
 | 
						|
    reset();
 | 
						|
 | 
						|
    x.emplace(b, b);
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
}
 | 
						|
 | 
						|
UNORDERED_AUTO_TEST(unnecessary_copy_emplace_map_test)
 | 
						|
{
 | 
						|
    // When calling 'source' the object is moved on some compilers, but not
 | 
						|
    // others. So count that here to adjust later.
 | 
						|
 | 
						|
    reset();
 | 
						|
    source<count_copies>();
 | 
						|
    int source_cost = ::unnecessary_copy_tests::count_copies::moves;
 | 
						|
 | 
						|
    reset();
 | 
						|
    source<std::pair<count_copies, count_copies> >();
 | 
						|
    int source_pair_cost = ::unnecessary_copy_tests::count_copies::moves;
 | 
						|
 | 
						|
    //
 | 
						|
 | 
						|
    reset();
 | 
						|
    boost::unordered_map<count_copies, count_copies> x;
 | 
						|
    // TODO: Run tests for pairs without const etc.
 | 
						|
    std::pair<count_copies const, count_copies> a;
 | 
						|
    x.emplace(a);
 | 
						|
    COPY_COUNT_EXTRA(4, 1);
 | 
						|
    MOVE_COUNT_EXTRA(0, 1);
 | 
						|
 | 
						|
//
 | 
						|
// 0 arguments
 | 
						|
//
 | 
						|
 | 
						|
#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100))
 | 
						|
    // 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_EXTRA(2, 1);
 | 
						|
    MOVE_COUNT_EXTRA(0, 1);
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
    reset();
 | 
						|
    x.emplace(boost::unordered::piecewise_construct, boost::make_tuple(),
 | 
						|
        boost::make_tuple());
 | 
						|
    COPY_COUNT(2);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
    //
 | 
						|
    // 1 argument
 | 
						|
    //
 | 
						|
 | 
						|
    reset();
 | 
						|
    x.emplace(a);
 | 
						|
    COPY_COUNT(0);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
    // A new object is created by source, but it shouldn't be moved or
 | 
						|
    // copied.
 | 
						|
    reset();
 | 
						|
    x.emplace(source<std::pair<count_copies, count_copies> >());
 | 
						|
    COPY_COUNT(2);
 | 
						|
    MOVE_COUNT(source_pair_cost);
 | 
						|
 | 
						|
#if !(defined(__GNUC__) && __cplusplus < 199900L) &&                           \
 | 
						|
    !(defined(_MSC_VER) && _MSC_VER < 1600)
 | 
						|
    count_copies part;
 | 
						|
    reset();
 | 
						|
    std::pair<count_copies const&, count_copies const&> a_ref(part, part);
 | 
						|
    x.emplace(a_ref);
 | 
						|
    COPY_COUNT(2);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
    // No move should take place.
 | 
						|
    // (since a is already in the container)
 | 
						|
    reset();
 | 
						|
    x.emplace(boost::move(a));
 | 
						|
    COPY_COUNT(0);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
    //
 | 
						|
    // 2 arguments
 | 
						|
    //
 | 
						|
 | 
						|
    std::pair<count_copies const, count_copies> b;
 | 
						|
 | 
						|
    reset();
 | 
						|
    x.emplace(b.first, b.second);
 | 
						|
    COPY_COUNT(0);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
    reset();
 | 
						|
    x.emplace(source<count_copies>(), source<count_copies>());
 | 
						|
    COPY_COUNT(2);
 | 
						|
    MOVE_COUNT(source_cost * 2);
 | 
						|
 | 
						|
    // source<count_copies> creates a single copy.
 | 
						|
    reset();
 | 
						|
    x.emplace(b.first, source<count_copies>());
 | 
						|
    COPY_COUNT(1);
 | 
						|
    MOVE_COUNT(source_cost);
 | 
						|
 | 
						|
    reset();
 | 
						|
    x.emplace(count_copies(b.first.tag_), count_copies(b.second.tag_));
 | 
						|
    COPY_COUNT(2);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
    reset();
 | 
						|
    x.emplace(boost::unordered::piecewise_construct,
 | 
						|
        boost::make_tuple(boost::ref(b.first)),
 | 
						|
        boost::make_tuple(boost::ref(b.second)));
 | 
						|
    COPY_COUNT(0);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
#if !defined(BOOST_NO_CXX11_HDR_TUPLE) || defined(BOOST_HAS_TR1_TUPLE)
 | 
						|
 | 
						|
    reset();
 | 
						|
    x.emplace(boost::unordered::piecewise_construct,
 | 
						|
        std::make_tuple(std::ref(b.first)),
 | 
						|
        std::make_tuple(std::ref(b.second)));
 | 
						|
    COPY_COUNT(0);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
 | 
						|
    std::pair<count_copies const, count_copies> move_source_trial;
 | 
						|
    reset();
 | 
						|
    std::make_tuple(std::move(move_source_trial.first));
 | 
						|
    std::make_tuple(std::move(move_source_trial.second));
 | 
						|
    int tuple_move_cost = ::unnecessary_copy_tests::count_copies::moves;
 | 
						|
    int tuple_copy_cost = ::unnecessary_copy_tests::count_copies::copies;
 | 
						|
 | 
						|
    std::pair<count_copies const, count_copies> move_source;
 | 
						|
    reset();
 | 
						|
    x.emplace(boost::unordered::piecewise_construct,
 | 
						|
        std::make_tuple(std::move(move_source.first)),
 | 
						|
        std::make_tuple(std::move(move_source.second)));
 | 
						|
    COPY_COUNT(tuple_copy_cost);
 | 
						|
    MOVE_COUNT(tuple_move_cost);
 | 
						|
 | 
						|
#if !defined(BOOST_NO_CXX11_HDR_TUPLE) &&                                      \
 | 
						|
    !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6) &&             \
 | 
						|
    !(defined(BOOST_MSVC) && BOOST_MSVC < 1700)
 | 
						|
    reset();
 | 
						|
    x.emplace(boost::unordered::piecewise_construct,
 | 
						|
        std::forward_as_tuple(b.first), std::forward_as_tuple(b.second));
 | 
						|
    COPY_COUNT(0);
 | 
						|
    MOVE_COUNT(0);
 | 
						|
#endif
 | 
						|
 | 
						|
#endif
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
RUN_TESTS()
 |