【问题标题】:Need some help understanding C++11 Move Constructors [duplicate]需要一些帮助来理解 C++11 移动构造函数 [重复]
【发布时间】:2014-09-03 12:28:01
【问题描述】:

作为一个 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)优化了移动构造器吗?或者这里还有什么其他的魔法?

那么有人可以解释一下上面的代码到底发生了什么吗?代码做了我想要的,但我真的不明白为什么。

【问题讨论】:

  • 搜索“返回值优化”(RVO),这个已经讨论过很多了(包括这里)。

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


【解决方案1】:

我认为这就是您所说的复制省略(即阻止对象的复制)并直接使用它。见:copy elision: move constructor not called when using ternary expression in return statement?

【讨论】:

  • 不是同样的问题,因为在您的链接中,return a; 语句调用了 move 构造函数,这不是这里的情况,这就是 OP 问他的问题的原因。
【解决方案2】:

你是Return Value Optimization这里的“受害者”。

请注意:“在 C++ 中,特别值得注意的是允许更改生成程序的可观察行为”。

编辑:因此,允许编译器应用优化,即使 move-ctor (cout) 的副作用已更改。

【讨论】:

    【解决方案3】:

    您的程序中可能会发生两个动作:

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

    出于相同的原因,编译器可以省略这两个动作:

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

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

    请注意,编译器可以完全优化任何东西,只要它不改变程序的行为(根据as-if 规则)。 复制/移动省略在标准中明确提及的原因是,如果您的复制/移动构造函数有副作用,它可能会改变程序的行为。即使编译器改变了程序的行为,编译器也可以执行此优化。这就是为什么您的复制/移动构造函数不应有副作用,因为这样您的程序将有多个有效的执行路径。

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

    【讨论】:

    • 在使用 -fno-elide-constructors 选项编译代码后,我终于能够看到移动构造函数的效果,并且我还能够使用移动赋值运算符,我想我终于现在理解了 C++11 的移动语义。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2012-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-10
    • 2012-01-05
    • 2012-02-02
    相关资源
    最近更新 更多