元组/领带的返回值优化

tho*_*san 17 c++ rvo c++11 stdtuple

我正在考虑在元组/联系的情况下返回值优化,我观察到的行为并不像我预期的那样.在下面的例子中,我希望移动语义能够启动,但它有一个复制操作.以下优化的输出是:

Test duo output, non_reference tuple
Default constructor invoked
Parameter constructor invoked
Copy constructor invoked
Move Assignment operator invoked
100
Run Code Online (Sandbox Code Playgroud)

在函数内部创建元组时复制构造函数的调用似乎是不必要的.有什么办法可以删除吗?我正在使用MSVC 2012编译器.

#include <iostream>
#include <tuple>

class A
{
public:
     int value;
     A() : value(-1)
     {
         std::cout << "Default constructor invoked" << std::endl;
     }

     explicit A(const int v) : value(v)
     {
         std::cout << "Parameter constructor invoked" << std::endl;
     }

     A(const A& rhs)
     {
         value = rhs.value;
         std::cout << "Copy constructor invoked" << std::endl;
     }

     A(const A&& rhs)
     {
         value = rhs.value;
         std::cout << "Move constructor invoked" << std::endl;
     }

     A& operator=(const A& rhs)
     {
         value = rhs.value;
         std::cout << "Assignment operator invoked" << std::endl;
         return *this;
     }

     A& operator=(const A&& rhs)
     {
         value = rhs.value;
         std::cout << "Move Assignment operator invoked" << std::endl;
         return *this;
     }
 };

 std::tuple<A, int> return_two_non_reference_tuple()
 {
     A tmp(100);

     return std::make_tuple(tmp, 99);
 }

 int main(int argc, char* argv[])
 {

      std::cout << "Test duo output, non_reference tuple" << std::endl;    
      A t3;
      int v1;
      std::tie(t3, v1) = return_two_non_reference_tuple();
      std::cout << t3.value << std::endl << std::endl;

      system("pause");
      return 0;
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*vic 7

由于您正在调用,因此不会自动调用移动构造函数

std::make_tuple(tmp, 99);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,tmp是左值.您可以使用std::move它将其转换为右值引用:

return std::make_tuple(std::move(tmp), 99);
Run Code Online (Sandbox Code Playgroud)

这将指示编译器使用move构造函数.


Tar*_*ama 5

副本出现在这里:

std::make_tuple(tmp, 99);
Run Code Online (Sandbox Code Playgroud)

虽然您可以看到tmp可以在元组中直接构造,但是从元组到副本的复制tmp不会被省略.你真正想要的是一种传递参数的方法,用于std::tuple构造它的内部Aint对象.没有这样的事情std::tuple,但有一种方法可以达到同样的效果.

由于您只有两种类型,您可以使用std::pair.它有一个std::piecewise_construct构造函数,它std::tuples包含两个包含传递给内部对象构造函数的参数.

 std::pair<A, int> return_two_non_reference_tuple()
 {
     return {std::piecewise_construct, 
             std::make_tuple(100), std::make_tuple(99)};
 }
Run Code Online (Sandbox Code Playgroud)

这个解决方案的一个很酷的事情是你仍然可以std::tie在呼叫站点使用,因为std::tuple有一个赋值运算符std::pair.

std::tie(t3, v1) = return_two_non_reference_tuple();
Run Code Online (Sandbox Code Playgroud)

正如您从输出中看到的那样,您的副本已经消失.它没有被其他答案中的移动取代,它被完全删除:

测试二重奏输出,非参考元组

调用了默认构造函数

调用了参数构造函数

调用了Move Assignment运算符

100

Live Demo