move/test/copy_elision_test.cpp
2014-09-25 16:45:22 +02:00

178 lines
5.4 KiB
C++

// Copyright David Abrahams 2009. 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)
#include <boost/move/detail/config_begin.hpp>
#include <iostream>
#include <boost/core/lightweight_test.hpp>
#ifdef NO_MOVE
# undef BOOST_COPY_ASSIGN_REF
# define BOOST_COPY_ASSIGN_REF(X) X const&
# undef BOOST_COPYABLE_AND_MOVABLE
# define BOOST_COPYABLE_AND_MOVABLE(X)
# define MOVE(x) (x)
#else
#include <boost/move/utility_core.hpp>
# define MOVE(x) boost::move(x)
#endif
struct X
{
X() : id(instances++)
{
std::cout << "X" << id << ": construct\n";
}
X(X const& rhs) : id(instances++)
{
std::cout << "X" << id << ": <- " << "X" << rhs.id << ": **copy**\n";
++copies;
}
// This particular test doesn't exercise assignment, but for
// completeness:
X& operator=(BOOST_COPY_ASSIGN_REF(X) rhs)
{
std::cout << "X" << id << ": <- " << "X" << rhs.id << ": assign\n";
return *this;
}
#ifndef NO_MOVE
X& operator=(BOOST_RV_REF(X) rhs)
{
std::cout << "X" << id << ": <- " << "X" << rhs.id << ": move assign\n";
return *this;
}
X(BOOST_RV_REF(X) rhs) : id(instances++)
{
std::cout << "X" << id << ": <- " << "X" << rhs.id << ": ..move construct..\n";
++copies;
}
#endif
~X() { std::cout << "X" << id << ": destroy\n"; }
unsigned id;
static unsigned copies;
static unsigned instances;
BOOST_COPYABLE_AND_MOVABLE(X)
};
unsigned X::copies = 0;
unsigned X::instances = 0;
#define CHECK_COPIES( stmt, min, max, comment ) \
{ \
unsigned const old_copies = X::copies; \
\
std::cout << "\n" comment "\n" #stmt "\n===========\n"; \
{ \
stmt; \
} \
unsigned const n = X::copies - old_copies; \
volatile unsigned const minv(min), maxv(max); \
BOOST_TEST(n <= maxv); \
if (n > maxv) \
std::cout << "*** max is too low or compiler is buggy ***\n"; \
BOOST_TEST(n >= minv); \
if (n < minv) \
std::cout << "*** min is too high or compiler is buggy ***\n"; \
\
std::cout << "-----------\n" \
<< n << "/" << max \
<< " possible copies/moves made\n" \
<< max - n << "/" << max - min \
<< " possible elisions performed\n\n"; \
\
if (n > minv) \
std::cout << "*** " << n - min \
<< " possible elisions missed! ***\n"; \
}
struct trace
{
trace(char const* name)
: m_name(name)
{
std::cout << "->: " << m_name << "\n";
}
~trace()
{
std::cout << "<-: " << m_name << "\n";
}
char const* m_name;
};
void sink(X)
{
trace t("sink");
}
X nrvo_source()
{
trace t("nrvo_source");
X a;
return a;
}
X urvo_source()
{
trace t("urvo_source");
return X();
}
X identity(X a)
{
trace t("identity");
return a;
}
X lvalue_;
X& lvalue()
{
return lvalue_;
}
typedef X rvalue;
X ternary( bool y )
{
X a, b;
return MOVE(y?a:b);
}
int main(int argc, char* argv[])
{
(void)argv;
// Double parens prevent "most vexing parse"
CHECK_COPIES( X a(( lvalue() )), 1U, 1U, "Direct initialization from lvalue");
CHECK_COPIES( X a(( rvalue() )), 0U, 1U, "Direct initialization from rvalue");
CHECK_COPIES( X a = lvalue(), 1U, 1U, "Copy initialization from lvalue" );
CHECK_COPIES( X a = rvalue(), 0U, 1U, "Copy initialization from rvalue" );
CHECK_COPIES( sink( lvalue() ), 1U, 1U, "Pass lvalue by value" );
CHECK_COPIES( sink( rvalue() ), 0U, 1U, "Pass rvalue by value" );
CHECK_COPIES( nrvo_source(), 0U, 1U, "Named return value optimization (NRVO)" );
CHECK_COPIES( urvo_source(), 0U, 1U, "Unnamed return value optimization (URVO)" );
// Just to prove these things compose properly
CHECK_COPIES( X a(urvo_source()), 0U, 2U, "Return value used as ctor arg" );
// Expect to miss one possible elision here
CHECK_COPIES( identity( rvalue() ), 0U, 2U, "Return rvalue passed by value" );
// Expect to miss an elision in at least one of the following lines
CHECK_COPIES( X a = ternary( argc == 1000 ), 0U, 2U, "Return result of ternary operation" );
CHECK_COPIES( X a = ternary( argc != 1000 ), 0U, 2U, "Return result of ternary operation again" );
return boost::report_errors();
}
#include <boost/move/detail/config_end.hpp>