-3

由于来自社区的广泛响应,我提出这个问题是希望能够揭穿来自堆栈溢出用户的特定于实现的响应。

其中哪一个是最佳实践(提供最大的优化)?

// 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 的直接、尊重的回答。[接受答案]

4

1 回答 1

2
// 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和返回值。

于 2017-07-27T00:25:28.353 回答