按值返回的函数的值类别总是xvalue

Is value category of function returning by value is always xvalue?

本文关键字:xvalue 函数 返回      更新时间:2023-10-16

根据我的理解,下面的代码应该调用移动Test类的构造函数,因为这个函数是按值返回的,这意味着表达式GetTestObj()应该是右值,xvalue是隐式移动的,但是为什么这个代码调用复制构造函数?

class Test
{
  public:
         Test()
         {
         }
         Test(const Test& arg)
         {
            std::cout<<"Copy Constructor Called..."<<std::endl;
         }
         Test(Test&& arg)
         {
            std::cout<<"Move Constructor Called..."<<std::endl;
         }
};
Test  GetMyTestObj()
{
      Test *ptr = new Test();
      return *ptr;
}
Test dummy = GetMyTestObj(); //Copy Constructor Called...

在您的代码中,实际上有一个拷贝从*ptr到返回值,一个移动从GetMyTestObj()dummy。但是,编译器会忽略这个移动,所以您不会看到它被跟踪。如果您将-fno-elide-constructors传递给GCC或Clang,那么您应该看到副本和移动(演示)。

如果你想构造一个移动的返回值,你需要使用std::move:

Test GetMyTestObj()
{
      Test *ptr = new Test();
      return std::move(*ptr);
}

然而,在这个实例中确实不需要动态分配;它效率低下,并且在实现过程中会泄漏内存。你应该使用一个自动变量:

Test GetMyTestObj()
{
    Test test;
    //I assume you're doing something else here
    return test;
}

使用上面的代码,编译器实际上可以省略这两个结构。

如果你在函数中没有做任何其他事情,你应该直接构造dummy:

Test dummy{};

value不在这里复制(由于复制省略,没有调用构造函数):

Test dummy = GetMyTestObj();

但这里:

return *ptr;

因为函数必须从左值引用生成右值对象。基本上,GetMyTestObj()函数在这种情况下相当于:

Test *ptr = new Test();
Test returnValue(*ptr);
return returnValue;

这意味着表达式GetTestObj()应该是右值,xvalue被隐式移动

首先直接回答你的问题,GetTestObj()是一个右值。

  • 非引用返回类型的函数调用或重载操作符表达式,如str.substr(1, 2)str1 + str2it++;

关键是return *ptr;需要复制/移动操作;ptr是一个命名变量,为左值。*ptr也是左值,它不能移动。

您可以使用std::move显式地将其设置为可移动的xvalue:

return std::move(*ptr);