请考虑以下代码段:
#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。
如何删除未使用的移动构造函数会导致这种情况?
答案 0 :(得分:4)
请注意,在copy elision优化中,复制/移动构造函数仍然必须存在且可访问。并且无法保证在每种情况下都会执行复制省略。
(强调我的)
即使发生了复制省略并且复制/移动构造函数是 没有被叫,它必须存在且可访问(好像没有优化 发生在所有),否则程序就会形成错误。
对于您的代码,复制构造函数已被delete
编辑,如果删除了移动构造函数的定义,并且不会隐式声明它,因为类A
具有用户定义的析构函数,那么移动/复制构造函数都不存在且无法访问,这就是编译失败的原因。
总之,这里需要复制/移动语法,编译器会检查它。然后编译器将决定是否执行复制省略(例如,在调试模式下)。
BTW:您可以使用-fno-elide-constructors
与clang和gcc禁止它。
答案 1 :(得分:3)
您需要记住(N)RVO是一种优化。即使它启动,代码也应该符合标准,它表示使用复制(或移动)构造函数构造值。即使最终没有调用构造函数,它也必须可用。
如果由于优化而无法调用,那么有一个建议允许缺少/不可用的构造函数,但我怀疑它是否会被实现。
答案 2 :(得分:-3)
如果你有复制构造函数,如果我没记错的话,你也必须有移动构造函数。