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

How implementing move constructor affects return value optimization?

考虑以下代码片段:

#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++ 都拒绝编译它,因为 class A 的复制构造函数被标记为已删除,因此似乎没有发生 RVO .

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

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

有人提议允许 missing/unavailable 构造函数,如果由于优化而不会被调用的话,但我怀疑它会被实施。

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

(强调我的)

Even when copy elision takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.

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

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

顺便说一句:您可以使用 -fno-elide-constructors 和 clang 和 gcc 来禁止它。

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