需要一些帮助才能理解C++ 11 Move Constructors

kay*_*ahr 8 c++ move-semantics c++11

作为一个C++新手,我在理解C++ 11的新Move-Constructor时遇到了问题,我希望有人可以解释我偶然发现的具体情况.我们来看看这个示例代码:

#include <iostream>

using namespace std;

class Model {
public:
    int data;
    Model(int data) : data(data) { cout << "Constructor" << endl; }
    Model(Model&& model) { cout << "Move constructor" << endl; }
    ~Model() { cout << "Destructor" << endl; }

private:
    Model(const Model& model);
    const Model& operator=(const Model&);

};

Model createModel(int data) {
    return Model(data);
}

int main(void) {
    Model model = createModel(1);
    cout << model.data << endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

所以我创建了一个createModel函数,它应该将模型作为临时右值返回,我想将它分配给左值.我不希望编译器创建Model对象的副本,因此我将复制构造函数定义为私有,并且我对赋值运算符执行相同操作以确保不复制任何数据.执行此操作后,代码正确不再编译,因此我添加了Move构造函数,现在它再次编译.但是当我运行程序时,我得到了这个输出:

Constructor
1
Destructor
Run Code Online (Sandbox Code Playgroud)

所以从未调用过移动构造函数.我不明白为什么我必须指定移动构造函数,以便在运行时根本不使用它时能够编译该程序.

是因为编译器(GCC 4.8.2)优化了移动构造函数吗?或者这里有其他魔术吗?

那么有人可以解释一下上面的代码究竟发生了什么?代码做我想要的但我真的不明白为什么.

Jos*_*eld 7

您的计划可能会发生两件事:

  1. 从函数到返回对象.
  2. 从返回对象到model.

出于同样的原因,编译器可以省略这两个步骤:

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

还有其他情况会发生复制/移动省略(参见C++ 11中的§12.8/ 31).请注意,复制/移动省略完全是可选的 - 编译器不必这样做.

请注意,只要编译器不会改变程序的行为(在as-if规则下),就允许编译器绝对优化任何东西.标准中明确提到复制/移动省略的原因是,如果复制/移动构造函数有副作用,它可能会改变程序的行为.即使编译器改变了程序的行为,也允许编译器执行此优化.这就是为什么你的复制/移动构造函数永远不应该有副作用,因为那时你的程序将有多个有效的执行路径.

您可以传递该-fno-elide-constructors选项以gcc确保永远不会执行此优化.