由于来自社群的广泛响应,我要求这样做,希望能够从堆栈溢出用户中揭示特定于实现的响应。
哪些是最佳实践(提供最佳优化)?
// version 1
MyObject Widget::GetSomething() {
return MyObject();
}
// version 2
MyObject Widget::GetSomething() {
return std::move(MyObject());
}
// version 3
MyObject Widget::GetSomething() {
auto obj = MyObject()
return obj;
}
// version 4
MyObject Widget::GetSomething() {
auto obj = MyObject()
return std::move(obj);
}
修改 感谢Yakk的直接,尊重的回答。 [接受回答]
答案 0 :(得分:2)
// version 1
MyObject Widget::GetSomething() {
return MyObject();
}
在C ++ 03中,这需要MyObject
可复制。在运行时,不会使用任何具有合理设置的“真实”编译器进行复制,因为此处标准允许省略。
在C ++ 11或14中,它要求对象是可移动的或可复制的。 Elision仍然存在;没有移动或复制。
在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(以及C ++ 03中的2个副本)中省略了2个动作。第一个省略是安全的,第二个是脆弱的。
// version 4
MyObject Widget::GetSomething() {
auto obj = MyObject();
return std::move(obj);
}
在实践中,这与版本2的行为相似。在构造obj
时会发生额外的移动(C ++ 03中的复制)但是它被省略了,所以在运行时没有任何事情发生。
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
和返回值一起移动。