c++ - 对临时与返回值优化的 const 引用

标签 c++ optimization return-value-optimization temporary-objects const-reference

我知道将右值分配给 const 左值引用会延长临时对象的生命周期,直到作用域结束。但是,我不清楚何时使用它以及何时依赖返回值优化。

LargeObject lofactory( ... ) {
     // construct a LargeObject in a way that is OK for RVO/NRVO
}

int main() {
    const LargeObject& mylo1 = lofactory( ... ); // using const&
    LargeObject mylo2 = lofactory( ... ); // same as above because of RVO/NRVO ?
}

根据 Scot Meyers 的“更有效的 C++”(第 20 条),编译器可以优化第二种方法以在适当的位置构造对象(这将是理想的,并且正是人们试图通过 const&在第一种方法中)。

  1. 是否有任何普遍接受的规则或最佳实践何时使用 const& 来临时以及何时依赖 RVO/NRVO?
  2. 会不会有使用const&方法比不使用更糟糕的情况? (例如,如果 LargeObject 实现了那些,我正在考虑 C++11 移动语义......)

最佳答案

让我们考虑最简单的情况:

lofactory( ... ).some_method();

在这种情况下,从 lofactory 到调用者上下文的拷贝是可能的 - 但可以通过 RVO/NRVO 优化掉。


LargeObject mylo2 ( lofactory( ... ) );

在这种情况下,可能的拷贝是:

  1. lofactory返回临时到调用者上下文——可以被RVO/NRVO
  2. 优化掉
  3. temporary 复制构造 mylo2 - 可以通过 copy-elision
  4. 优化掉

const LargeObject& mylo1 = lofactory( ... );

在这种情况下,仍然可以复制一份:

  1. 临时lofactory返回到调用者上下文——可以通过RVO/NRVO优化掉(也是!)

一个引用将绑定(bind)到这个临时。


所以,

Are there any generally accepted rules or best practices when to use const& to temporaries and when to rely on RVO/NRVO?

如上所述,即使在使用 const& 的情况下,也可能会出现不必要的拷贝,并且可以通过 RVO/NRVO 对其进行优化。

如果您的编译器在某些情况下应用 RVO/NVRO,那么它很可能会在第 2 阶段(如上)执行复制省略。因为在这种情况下,复制省略比 NRVO 简单得多。

但是,在最坏的情况下,const& 的情况下您将拥有一份拷贝,而当您初始化值时,您将拥有两份。

Could there be a situation in which using the const& method is worse than not using it?

我不认为有这样的情况。至少除非你的编译器使用奇怪的规则来区分 const&。 (举个类似情况的例子,我注意到 MSVC 不做 NVRO 来进行聚合初始化。)

(I'm thinking for example about C++11 move semantics if LargeObject has those implemented ...)

在 C++11 中,如果 LargeObject 具有移动语义,那么在最坏的情况下,const& 的情况下你会移动一次,而当你初始化值。所以,const& 还是好一点的。


So a good rule would be to always bind temporaries to const& if possible, since it might prevent a copy if the compiler fails to do a copy-elision for some reason?

在不知道应用程序的实际上下文的情况下,这似乎是一个很好的规则。

在 C++11 中,可以将临时绑定(bind)到右值引用 - LargeObject&&。所以,这样的临时性是可以修改的。


顺便说一句,移动语义仿真可通过不同的技巧在 C++98/03 中使用。例如:

但是,即使存在移动语义 - 也有无法廉价移动的对象。例如,内部带有双数据[4][4] 的 4x4 矩阵类。因此,即使在 C++11 中,复制省略 RVO/NRVO 仍然非常重要。顺便说一句,当复制省略/RVO/NRVO 发生时 - 它比移动更快。


P.S.,在实际情况下,还有一些额外的事情需要考虑:

例如,如果您有返回 vector 的函数,即使应用了 Move/RVO/NRVO/Copy-Elision - 它仍然可能不是 100% 有效。例如,考虑以下情况:

while(/*...*/)
{
    vector<some> v = produce_next(/* ... */); // Move/RVO/NRVO are applied
    // ...
}

将代码更改为:

vector<some> v;
while(/*...*/)
{
    v.clear();

    produce_next( v ); // fill v
    // or something like:
    produce_next( back_inserter(v) );
    // ...
}

因为在这种情况下,当 v.capacity() 足够时,可以重新使用 vector 中已分配的内存,而无需在每次迭代时在 producer_next 中进行新的分配。

关于c++ - 对临时与返回值优化的 const 引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13318257/

相关文章:

c++ - 使蒙版外全部透明

c++ - C++ 编译器可以自动为我优化 float 为 double 吗?

c++ - Qt为所有对话框设置通用背景

c++ - 为什么 GCC 不能优化 `std::sqrt` ?

c++ - 如何优化将此数据写入 postgres 数据库

c++ - 什么是复制省略和返回值优化?

c++ - 我是否正确测试了返回值优化?

c++11返回值优化还是搬家?

c++ - 我可以在 C++ 中从另一个构造函数调用构造函数(进行构造函数链接)吗?

c++ - Qt 如何绘制它的 GUI 组件(基本思路)?