实现移动构造函数如何影响返回值优化?

时间:2016-06-01 16:02:36

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

请考虑以下代码段:

#include <iostream>
#include <string>

class A { 
 public:
    A() { 
        std::cout << "A::A()\n";
    } 

    ~A() { 
        std::cout << "A::~A()\n";
    } 

    A(const A&) = delete;

    A(A&&) { 
        std::cout << "A::A(A&&)\n";
    };

};

A f() { 
    A a;
    return a;
}

int main() { 
    A a = f();
    return 0;
}

它与g++clang++编译良好,输出为

A::A()
A::~A()

在这种情况下,似乎RVO开始了。请注意,没有调用移动构造函数。

但是,如果有人从上面的代码中删除那个未使用的移动构造函数,那么代码段会变为:

#include <iostream>
#include <string>

class A { 
 public:
    A() { 
        std::cout << "A::A()\n";
    } 

    ~A() { 
        std::cout << "A::~A()\n";
    } 

    A(const A&) = delete;

};

A f() { 
    A a;
    return a;
}

int main() { 
    A a = f();
    return 0;
}

clang++g++都拒绝编译,因为类A的copy-constructor被标记为已删除,因此似乎没有发生RVO。

如何删除未使用的移动构造函数会导致这种情况?

3 个答案:

答案 0 :(得分:4)

请注意,在copy elision优化中,复制/移动构造函数仍然必须存在且可访问。并且无法保证在每种情况下都会执行复制省略。

(强调我的)

  

即使发生了复制省略并且复制/移动构造函数是   没有被叫,它必须存在且可访问(好像没有优化   发生在所有),否则程序就会形成错误。

对于您的代码,复制构造函数已被delete编辑,如果删除了移动构造函数的定义,并且不会隐式声明它,因为类A具有用户定义的析构函数,那么移动/复制构造函数都不存在且无法访问,这就是编译失败的原因。

总之,这里需要复制/移动语法,编译器会检查它。然后编译器将决定是否执行复制省略(例如,在调试模式下)。

BTW:您可以使用-fno-elide-constructors与clang和gcc禁止它。

答案 1 :(得分:3)

您需要记住(N)RVO是一种优化。即使它启动,代码也应该符合标准,它表示使用复制(或移动)构造函数构造值。即使最终没有调用构造函数,它也必须可用。

如果由于优化而无法调用,那么有一个建议允许缺少/不可用的构造函数,但我怀疑它是否会被实现。

答案 2 :(得分:-3)

如果你有复制构造函数,如果我没记错的话,你也必须有移动构造函数。