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

4

请考虑以下代码片段:

#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;
}

由于类A的复制构造函数被标记为删除,因此clang++g++都拒绝编译此代码,所以似乎没有RVO优化发生。

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


对我来说编译正常。使用的是Apple LLVM version 7.3.0 (clang-703.0.31)。此外,http://ideone.com/xTpD56。 - miguel.martin
如果您声明一个复制构造函数,那么不会隐式地声明移动构造函数。 - Kerrek SB
3个回答

4
请注意,在复制省略优化中,复制/移动构造函数仍然必须存在且可访问。并且不能保证每种情况都会执行复制省略。
(强调是我的)
即使发生复制省略并且未调用复制/移动构造函数,它也必须存在且可访问(就像没有进行任何优化一样),否则程序就不符合规范。
对于您的代码,复制构造函数已被删除,如果您删除移动构造函数的定义,并且因为类A有一个用户定义的析构函数而不会被隐式声明,那么移动/复制构造函数都不存在且不可访问,这就是编译失败的原因。
总之,在这里需要使用复制/移动语法,并且编译器将检查它。然后编译器将决定是否执行复制省略(例如在调试模式下)。
顺便说一句:您可以在clang和gcc中使用-fno-elide-constructors来禁止它。

我不明白编译器在应用 RVO 之前如何选择使用哪个构造函数(复制或移动)来返回函数中的本地变量。这是否已经定义? - Anton K
@Anton编译器正在使用最佳匹配。如果该值是右值,并且可用移动构造函数,那么它将被调用。如果不是右值,或移动构造函数不可用,则将使用复制构造函数。在这方面,它与常规函数的重载解析没有区别。 - SergeyA
@Anton 是的,哪一个被选中已经定义好了。对于你的代码,移动构造函数将被选择,因为 f() 将返回一个可以绑定到右值引用的临时对象。不管怎样,似乎还有另一个问题。 - songyuanyao

3

请记住,(N)RVO是一种优化技术。即使它生效了,代码也应符合标准,该标准规定使用复制(或移动)构造函数构造值。即使最终没有调用构造函数,它也必须可用。

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


这是否意味着在第一个片段中,函数f的本地变量a通过移动语义被返回,然后优化器决定使用RVO并且放弃调用移动构造函数? - Anton K
很难说编译器中先发生什么,后发生什么 :) 但是代码必须首先正确,其次进行优化。 - SergeyA
让我们换个说法:第一段代码片段是正确的,因为移动构造函数被定义且可访问,这允许从 f 返回本地变量 a - Anton K
@Anton,没错。从本地值(或参数)返回将其视为临时值。 - SergeyA

-3
如果我没记错的话,如果你有拷贝构造函数,你也必须有移动构造函数。

这充其量只是一个评论(而且是错误的)。根本不是任何形式上的答案。 - SergeyA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接