2016-11-07 55 views
0

按下面的代码应调用Test类的移动构造函数,因为该功能是由价值回归我的理解,这意味着表达GetTestObj()应该是右值和xvalues都隐含移动,但为什么这个代码是调用拷贝构造函数?函数返回值的值类别总是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... 
+0

它非常依赖'Test'类的定义。你有没有尝试使复制构造函数被删除?你有一个移动构造函数?你知道你在显示的代码中有内存泄漏吗?为什么不简单地在函数中创建一个简单的'Test'对象实例(例如'Test test;')并返回它? –

回答

0

这意味着表达GetTestObj()应该是右值和xvalues隐式移动

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

  • 函数调用或非参考返回类型的重载操作表达式,如str.substr(1, 2)str1 + str2,或者it++;

被需要用于return *ptr;的点复制/移动操作; ptr是一个命名变量,它是一个lvalue*ptr也是左值,它不能移动。

你可以使用std::move明确,使之成为x值是可移动的:

return std::move(*ptr); 
+0

但是表达式的值类别是什么GetMyTestObj() – Kapil

+0

@Kapil这是一个prvalue;但这里没关系。复制发生在return语句中。 – songyuanyao

+0

'std :: move'在这里是不相关的,因为它隐式应用于返回值,除非出现副本删除。这在标准中描述。所以,基本上,添加'std :: move'来返回语句只能阻止优化 –

2

在你的代码实际上有来自*ptr一个拷贝到返回值和GetMyTestObj()一招dummy。但是,编译器elides的举动,让你不会看到它的踪迹。如果您通过-fno-elide-constructors到GCC或锵,那么你应该看到了复制和移动(demo)。

如果你想返回值的建设的举动,你需要使用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; 
} 

与上面的代码,编译器实际上两者的Elid结构。

如果你什么也不做该功能,您应该只是构建dummy直接:

Test dummy{}; 
0

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

Test dummy = GetMyTestObj(); 

但这里:

return *ptr; 

,因为函数必须使右值对象来回m左值参考。基本上,GetMyTestObj()在这种情况下的功能相当于:

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