【问题标题】:Problems with move constructor移动构造函数的问题
【发布时间】:2014-07-11 13:19:10
【问题描述】:

我正在测试一个移动构造函数并执行了以下操作:

#include <iostream>
#include <string>
using namespace std;

class X{
    public:
        int* p;
        int size;
        X(){}
        X(int n) : size(n){
            p = new int[n];
            for(int i = 0; i < n; i++)
                p[i] = i;
            cout << "Constructor\n";
        }
        ~X(){
            delete[] p;
        }
        X(const X& r){
            cout << "Copy\n";

        }
        X(X&& r){
            p = r.p;
            size = r.size;
            r.p = NULL;
            r.size = 0;
            cout << "Move\n";
        }
};
int main() {
    X a(10); //constructor    
    X b(a); // copy
    X c(X(3)); //constructor, move
    return 0;
}

我在输出中的预期是在 cmets 中,但是在编译(VS 2012)时没有调用移动构造函数?!但是,如果我向构造函数添加额外的参数:

string name;
X(int n, string _name) : size(n), name(_name){
    p = new int[n];
    for(int i = 0; i < n; i++)
        p[i] = i;
    cout << "Constructor\n";
}

然后

X a(10, "a"); //constructor
X b(a); // copy
X c(X(3, "pom")); //constructor, move

我得到了预期的结果...我真的不明白为什么。

编辑:现在在 GCC 4.7.2 上进行了测试,在这两种情况下它都不会调用 Move 构造函数,但 C++ Builder XE5 编译器在这两种情况下都会调用 Move 构造函数。然而,VS 只在第二种情况下调用它(当使用额外的构造函数参数时)。很有趣...

【问题讨论】:

  • 尝试使用std::move
  • 我知道 std::move 但这并不能回答我的问题。
  • 可能是编译器正在优化 X c(X(3)) 以将其转换为 X c(3)。允许编译器这样做。它本质上是一个返回值优化:即使在移动语义之前,如果一个函数按值返回一些东西给分配给它的调用者,我相信编译器只允许在堆栈上的最终位置分配一次变量并“假装"返回它。
  • Ben 的假设是可靠的。最好在 X c(X(3)) 处设置断点并查看反汇编和/或尝试不进行任何优化。
  • “当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数有副作用。” [如调试输出;-)] (12.8/31 复制和移动类对象)

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


【解决方案1】:

编译器省略了移动构造,直接将X(3)构造成c,本质上就是把你的初始化变成了

X c(3);

使用 gcc,您可以使用 -fno-elide-constructors 开关禁用此功能。添加后,原始示例的输出与预期的一样:

Constructor
Copy
Constructor
Move

Live demo

【讨论】:

    猜你喜欢
    • 2013-01-03
    • 2019-05-17
    • 2011-05-22
    • 1970-01-01
    • 2021-08-18
    • 1970-01-01
    • 1970-01-01
    • 2017-11-03
    • 1970-01-01
    相关资源
    最近更新 更多