为什么这个移动构造函数不起作用

时间:2013-09-17 12:32:30

标签: c++ c++11 move copy-constructor move-semantics

我有以下代码段。有谁知道为什么在主函数中没有为所有情况调用此移动构造函数?为什么要编译呢?赋值运算符是私有的吗?这里有链接:http://ideone.com/bZPnyY

#include <iostream>    
#include <vector>

class A{
public:
    A(int i){
        std::cout << "Constructor "<< i <<std::endl;
        for(int l = 0; l<i;l++){
            vec.push_back(l);
        }
    };

    A(A && ref): vec(std::move(ref.vec))
    {
       std::cout << "Move constructor"<<std::endl;
    }

    A & operator=(A && ref){
       if(this != &ref){
            vec = std::move(ref.vec);
       }
       std::cout << "Move assignment"<<std::endl;
       return *this;

    }

    std::vector<int> vec;

private:
    A(const A & ref);
    A(A & ref);
    A & operator=(A & ref);
};


A makeA(){
    A a(3);
    return a;
}

int main(){
    A b1(makeA()) ;
    A b2 = makeA();
    A b3 = A(3);
    A b4(A(3));
    std::cout << b4.vec[2] << std::endl;
};

输出:
构造函数3
构造函数3
构造函数3
构造函数3
2

回复的一些补充: 当我添加

 std::pair<int,A> a(3,A(3));

然后调用移动构造函数(所以希望没有NRVO)

2 个答案:

答案 0 :(得分:6)

您所看到的被称为 copy elision ,并记录在12.8.31中。

A makeA(){
    A a(3);
    return a;
}

A b1(makeA()) ;
A b2 = makeA();

此处,a中的makeA局部变量直接放在makeA的返回值中,而makeA的临时返回值直接放在b1中。 b2A b3 = A(3); A b4(A(3)); 的存储空间。 (连续两个副本)

A(3)

使用b3创建的临时文件直接在b4b1中构建。 (一份副本)

  

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

这是b2b1案例的第一部分:

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

这是b2b3案例的第二部分,以及b4和{{1}}案例的整个部分:

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

还有另外两个复制省略允许的情况与抛出和捕获异常有关。

答案 1 :(得分:1)

复制赋值运算符甚至无处可去,也不会混淆=令牌与赋值的混淆(例如,b1 = b2;)。

A b1(makeA()) ;  // direct initialization
A b2 = makeA();  // copy-initialization
A b3 = A(3);     // copy-initialization
A b4(A(3));      // direct initialization

虽然复制初始化要求良好,但要求可以访问复制或移动构造函数,编译器可以自由地删除它并直接初始化对象。关于哪个构造函数符合每个构造函数的细微差别,请阅读有关此here的更多信息。

如果您正在使用GCC,请尝试使用-fno-elide-constructors进行编译并查看差异。