copy_elision_test.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright David Abrahams 2009. Distributed under the Boost
  2. // Software License, Version 1.0. (See accompanying
  3. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  4. #include <boost/move/detail/config_begin.hpp>
  5. #include <iostream>
  6. #include <boost/core/lightweight_test.hpp>
  7. #ifdef NO_MOVE
  8. # undef BOOST_COPY_ASSIGN_REF
  9. # define BOOST_COPY_ASSIGN_REF(X) X const&
  10. # undef BOOST_COPYABLE_AND_MOVABLE
  11. # define BOOST_COPYABLE_AND_MOVABLE(X)
  12. # define MOVE(x) (x)
  13. #else
  14. #include <boost/move/utility_core.hpp>
  15. # define MOVE(x) boost::move(x)
  16. #endif
  17. struct X
  18. {
  19. X() : id(instances++)
  20. {
  21. std::cout << "X" << id << ": construct\n";
  22. }
  23. X(X const& rhs) : id(instances++)
  24. {
  25. std::cout << "X" << id << ": <- " << "X" << rhs.id << ": **copy**\n";
  26. ++copies;
  27. }
  28. // This particular test doesn't exercise assignment, but for
  29. // completeness:
  30. X& operator=(BOOST_COPY_ASSIGN_REF(X) rhs)
  31. {
  32. std::cout << "X" << id << ": <- " << "X" << rhs.id << ": assign\n";
  33. return *this;
  34. }
  35. #ifndef NO_MOVE
  36. X& operator=(BOOST_RV_REF(X) rhs)
  37. {
  38. std::cout << "X" << id << ": <- " << "X" << rhs.id << ": move assign\n";
  39. return *this;
  40. }
  41. X(BOOST_RV_REF(X) rhs) : id(instances++)
  42. {
  43. std::cout << "X" << id << ": <- " << "X" << rhs.id << ": ..move construct..\n";
  44. ++copies;
  45. }
  46. #endif
  47. ~X() { std::cout << "X" << id << ": destroy\n"; }
  48. unsigned id;
  49. static unsigned copies;
  50. static unsigned instances;
  51. BOOST_COPYABLE_AND_MOVABLE(X)
  52. };
  53. unsigned X::copies = 0;
  54. unsigned X::instances = 0;
  55. #define CHECK_COPIES( stmt, min, max, comment ) \
  56. { \
  57. unsigned const old_copies = X::copies; \
  58. \
  59. std::cout << "\n" comment "\n" #stmt "\n===========\n"; \
  60. { \
  61. stmt; \
  62. } \
  63. unsigned const n = X::copies - old_copies; \
  64. volatile unsigned const minv(min), maxv(max); \
  65. BOOST_TEST(n <= maxv); \
  66. if (n > maxv) \
  67. std::cout << "*** max is too low or compiler is buggy ***\n"; \
  68. BOOST_TEST(n >= minv); \
  69. if (n < minv) \
  70. std::cout << "*** min is too high or compiler is buggy ***\n"; \
  71. \
  72. std::cout << "-----------\n" \
  73. << n << "/" << max \
  74. << " possible copies/moves made\n" \
  75. << max - n << "/" << max - min \
  76. << " possible elisions performed\n\n"; \
  77. \
  78. if (n > minv) \
  79. std::cout << "*** " << n - min \
  80. << " possible elisions missed! ***\n"; \
  81. }
  82. struct trace
  83. {
  84. trace(char const* name)
  85. : m_name(name)
  86. {
  87. std::cout << "->: " << m_name << "\n";
  88. }
  89. ~trace()
  90. {
  91. std::cout << "<-: " << m_name << "\n";
  92. }
  93. char const* m_name;
  94. };
  95. void sink(X)
  96. {
  97. trace t("sink");
  98. }
  99. X nrvo_source()
  100. {
  101. trace t("nrvo_source");
  102. X a;
  103. return a;
  104. }
  105. X urvo_source()
  106. {
  107. trace t("urvo_source");
  108. return X();
  109. }
  110. X identity(X a)
  111. {
  112. trace t("identity");
  113. return a;
  114. }
  115. X lvalue_;
  116. X& lvalue()
  117. {
  118. return lvalue_;
  119. }
  120. typedef X rvalue;
  121. X ternary( bool y )
  122. {
  123. X a, b;
  124. return MOVE(y?a:b);
  125. }
  126. int main(int argc, char* argv[])
  127. {
  128. (void)argv;
  129. // Double parens prevent "most vexing parse"
  130. CHECK_COPIES( X a(( lvalue() )), 1U, 1U, "Direct initialization from lvalue");
  131. CHECK_COPIES( X a(( rvalue() )), 0U, 1U, "Direct initialization from rvalue");
  132. CHECK_COPIES( X a = lvalue(), 1U, 1U, "Copy initialization from lvalue" );
  133. CHECK_COPIES( X a = rvalue(), 0U, 1U, "Copy initialization from rvalue" );
  134. CHECK_COPIES( sink( lvalue() ), 1U, 1U, "Pass lvalue by value" );
  135. CHECK_COPIES( sink( rvalue() ), 0U, 1U, "Pass rvalue by value" );
  136. CHECK_COPIES( nrvo_source(), 0U, 1U, "Named return value optimization (NRVO)" );
  137. CHECK_COPIES( urvo_source(), 0U, 1U, "Unnamed return value optimization (URVO)" );
  138. // Just to prove these things compose properly
  139. CHECK_COPIES( X a(urvo_source()), 0U, 2U, "Return value used as ctor arg" );
  140. // Expect to miss one possible elision here
  141. CHECK_COPIES( identity( rvalue() ), 0U, 2U, "Return rvalue passed by value" );
  142. // Expect to miss an elision in at least one of the following lines
  143. CHECK_COPIES( X a = ternary( argc == 1000 ), 0U, 2U, "Return result of ternary operation" );
  144. CHECK_COPIES( X a = ternary( argc != 1000 ), 0U, 2U, "Return result of ternary operation again" );
  145. return boost::report_errors();
  146. }
  147. #include <boost/move/detail/config_end.hpp>