// version 1
MyObject Widget::GetSomething() {
return MyObject();
}
在 C++03MyObject
中,这需要可复制。在运行时,不会使用任何具有合理设置的“真实”编译器进行复制,因为标准允许此处省略。
在 C++11 或 14 中,它要求对象是可移动的或可复制的。省略号仍然存在;没有移动或复制。
在 C++17 中,这里没有移动或复制可以省略。
在每一种情况下,在实践中,MyObject
都是直接在返回值中构造的。
// version 2
MyObject Widget::GetSomething() {
return std::move(MyObject());
}
这在 C++03 中无效。
在 C++11 及更高版本中,MyObject
移动到返回值中。移动必须在运行时发生(除非消除)。
// version 3
MyObject Widget::GetSomething() {
auto obj = MyObject();
return obj;
}
与版本 1 相同,但 C++17 的行为类似于 C++11/14。此外,这里的省略更脆弱;看似无害的更改可能会迫使编译器实际移动obj
。
理论上,在 C++11/14/17 中省略了 2 个动作(在 C++03 中省略了 2 个副本)。第一个省略是安全的,第二个是脆弱的。
// version 4
MyObject Widget::GetSomething() {
auto obj = MyObject();
return std::move(obj);
}
在实践中,它的行为就像版本 2。在构造过程中发生了额外的移动(C++03 中的复制),obj
但它被省略了,因此在运行时没有任何反应。
Elision 允许消除复制/移动的副作用;对象的生命周期合并为一个对象,并消除了移动/复制。构造函数仍然必须存在,只是永远不会被调用。
回答
1 和 3 都将编译为相同的运行时代码。3 稍微脆弱一些。
2 和 4 都编译为相同的运行时代码。它永远不应该比 1/3 快,但是如果编译器可以消除移动,证明不这样做与这样做是一样的,它可以编译为与 1/3 相同的运行时代码。这远不能保证,而且非常脆弱。
在实践中从快到慢的顺序也是如此1>=3>=2>=4
,其中“更脆弱”的代码在其他方面的速度相同<=
。
作为一个可能使 3 比 1 慢的例子,如果你有一个 if 语句:
// version 3 - modified
MyObject Widget::GetSomething() {
auto obj = MyObject();
if (err()) return MyObject("err");
return obj;
}
突然间,许多编译器将被迫移入obj
返回值,而不是同时删除obj
和返回值。