作为一名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;
}
所以我创建了一个createModel
函数,它应该将模型作为临时右值返回,我想将它分配给左值。我不希望编译器创建Model
对象的副本,因此我将复制构造函数定义为私有,我对赋值运算符执行相同操作以确保不复制任何数据。执行此操作后,代码正确不再编译,因此我添加了Move构造函数,现在它再次编译。但是当我运行程序时,我得到了这个输出:
Constructor
1
Destructor
所以从未调用过移动构造函数。我不明白为什么我必须指定移动构造函数才能在运行时根本不使用它时编译程序。
是否因为编译器(GCC 4.8.2)优化了移动构造函数?或者这里有其他魔法吗?
有人可以解释一下上面代码中到底发生了什么吗?代码做了我想要的但我真的不明白为什么。
答案 0 :(得分:7)
您的计划可能会发生两件事:
model
。由于同样的原因,编译器可以省略这两个步骤:
当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-nonqualified类型的类对象时,可以通过直接构造临时对象来省略复制/移动操作进入省略的复制/移动目标
还有其他情况会发生复制/移动省略(参见C ++ 11中的§12.8/ 31)。请注意,复制/移动省略完全是可选的 - 编译器不必这样做。
请注意,只要不改变程序行为(在 as-if规则下),就允许编译器绝对优化任何内容。标准中明确提到复制/移动省略的原因是因为它可能会改变程序的行为,如果您的复制/移动构造函数有副作用。即使编译器改变了程序的行为,也允许编译器执行此优化。这就是你的复制/移动构造函数永远不应该有副作用的原因,因为那时你的程序将有多个有效的执行路径。
您可以将-fno-elide-constructors
选项传递给gcc
,以确保永远不会执行此优化。
答案 1 :(得分:1)
我认为这就是你所谓的复制省略(即防止对象的副本)并直接使用它。请参阅:copy elision: move constructor not called when using ternary expression in return statement?
答案 2 :(得分:1)
你是Return Value Optimization的“受害者”。
请注意:“在C ++中,特别值得注意的是,允许更改生成的程序的可观察行为”。
编辑:因此,即使move-ctor(cout)的副作用已经改变,也允许编译器应用优化。