临时T作为参数是否应该在C ++ 11中调用T(const T&)或T(T&&)?

时间:2014-06-11 20:41:09

标签: c++11 move-constructor temporary-objects

所以,首先是代码:

#include <iostream>
#include <utility>

struct X{
    int i;
    void transform(){}
    X() :i(0){std::cout<<"default\n";}
    X(const X& src): i(src.i){std::cout<<"copy\n";}
    X(X&& msrc) :i(msrc.i){msrc.i=0;std::cout<<"move\n";}
};

X getTransform(const X& src){
    X tx(src);
    tx.transform();
    return tx;
}

int main(){

    X x1;// default
    X x2(x1); // copy
    X x3{std::move(X{})}; // default then move
    X x41(getTransform(x2)); // copy in function ,then what?
    X x42(std::move(getTransform(x2))); // copy in funciton, then move
    X x51( (X()) );//default, then move? or copy?
      // extra() for the most vexing problem
    X x52(std::move(X())); //default then move
    std::cout<<&x41<<"\t"<<&x51<<std::endl;
}

然后从cygwin + gcc 4.8.2开始输出C ++ 11功能:

default
copy
default
move
copy
copy
move
default
default
move
0x22aa70        0x22aa50

我不太了解的是x41和x51的行。对于x41,是否应该从函数调用返回的临时调用移动构造函数或副本?同样的问题x51。 第二个问题是,通过查看输出,x41和x51的结构没有调用任何定义的构造函数,但是对象在它们驻留在内存中时清楚地创建。怎么会这样?

谢谢!

3 个答案:

答案 0 :(得分:6)

一个未命名的对象自然比&&更好地匹配const& 否则移动语义将无效。

现在,对默认/复制/移动构造函数的调用较少,然后人们可能天真地期望,因为有一个允许复制椭圆的特殊规则,而不考虑可观察的行为(否则必须通过优化保留) :

  

12.8复制和移动物体§31

     

当满足某些条件时,允许省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数具有副作用。在这种情况下,实现将省略的复制/移动操作的源和目标视为仅仅两种不同的引用同一对象的方式,并且该对象的销毁发生在两个对象的后期时间。在没有优化的情况下销毁.123在下列情况下允许复制/移动操作的省略,称为复制省略(可以合并以消除多份副本):
  但是,如果从函数返回并直接用于初始化相同类型的对象,则将省略该移动    - 具有类返回类型的函数中的在返回语句中,当表达式是具有相同cv-的非易失性自动对象(函数或catch子句参数除外)的名称时作为函数返回类型的非限定类型,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作。
   - 当未绑定到引用的临时类对象(12.2)将被复制/移动到具有相同cv-nonqualified类型的类对象时,可以省略复制/移动操作将临时对象直接构造到省略的复制/移动的目标中    - [...还有2个用于异常处理]

所以,浏览你的清单:

X x1;// default
// That's right
X x2(x1); // copy
// Dito
X x3{std::move(X{})}; // default then move
// Yes. Sometimes it does not pay to call `std::move`
X x41(getTransform(x2)); // copy in function ,then what?
// Copy in function, copy to output, move-construction to x41.
// RVO applies => no copy to output, and no dtor call for auto variable in function
// Copy ellision applies => no move-construction nor dtor of temporary in main
// So, only one time copy-ctor left
X x42(std::move(getTransform(x2))); // copy in funciton, then move
// `std::`move` is bad again
X x51( (X()) );//default, then move? or copy? // extra() for the most vexing problem
// Copy-elision applies: default+move+dtor of temporary
// will be optimized to just default
X x52(std::move(X())); //default then move
// And again `std::`move` is a pessimization

我认为使用static_cast可能会避免绑定临时,这意味着移动可以被省略,但没有这样的运气:1376. static_cast of temporary to rvalue reference谢谢@dyp for unearthing this issue

答案 1 :(得分:1)

我认为这只是简单的返回值优化。您在X tx(src);上的函数中创建了一个副本,然后将此局部变量返回给main。在语义上作为副本,但实际上省略了复制操作。

正如其他人所说,移动也可以省略。

答案 2 :(得分:1)

根据标准§12.8[复制和移动类对象]

  

31当满足某些条件时,允许实现省略类的复制/移动构造   对象,即使为复制/移动操作和/或对象的析构函数选择了构造函数   有副作用。在这种情况下,实现会处理省略的复制/移动的源和目标   操作只是两种不同的方式来引用同一个对象,以及对该对象的破坏   发生在两个对象在没有优化的情况下被破坏的时间的晚期.124   在以下情况下允许复制/移动操作的省略,称为复制省略(其中   可以合并以消除多个副本)

     
      
  • 在具有类返回类型的函数的return语句中,当表达式是a的名称时   具有相同cvunqualified的非易失性自动对象(函数或catch子句参数除外)   键入函数返回类型,可以通过构造省略复制/移动操作   自动对象直接进入函数的返回值。

  •   
  • 当一个尚未绑定到引用(12.2)的临时类对象被复制/移动时   对于具有相同cv-unqualified类型的类对象,可以省略复制/移动操作   将临时对象直接构造到省略的复制/移动

  • 的目标中   

因此,在这两种情况下(分别是x41x51),您正在体验复制省略优化效果。