4

我有以下代码片段。有谁知道为什么主函数中的所有情况都没有调用这个移动构造函数?为什么它仍然编译?赋值运算符是私有的?这里的链接: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)

4

2 回答 2

8

您所看到的称为复制省略,并记录在 12.8.31 中。

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

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

这里,a局部变量 inmakeA直接放置在 的返回值中makeA,而 的临时返回值又直接放置在andmakeA的存储中。(连续两次复制省略)b1b2

A b3 = A(3);
A b4(A(3));

使用创建的临时对象直接在和A(3)中构造。(一个副本省略)b3b4

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使为复制/移动操作选择的构造函数和/或对象的析构函数具有副作用。在这种情况下,实现将省略的复制/移动操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象本应被删除的较晚时间。在没有优化的情况下销毁。122 这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以结合起来消除多个副本):

这是b1andb2案例的第一部分:

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

这是and case的第二部分,也是and b1caseb2的整个部分:b3b4

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

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

于 2013-09-17T12:40:50.680 回答
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

虽然要形成良好的复制初始化需要可访问复制或移动构造函数,但编译器可以自由地省略它并直接初始化对象。每个构造函数适用于哪个构造函数存在细微差别,请在此处阅读更多信息。

如果您使用的是 GCC,请尝试编译-fno-elide-constructors并查看差异。

于 2013-09-17T12:52:39.083 回答