【问题标题】:Return value optimization: ho can I avoid copy construction of huge STL containers.返回值优化:我可以避免巨大的 STL 容器的复制构造。
【发布时间】:2015-11-06 08:50:29
【问题描述】:

当我想要一个函数返回一个容器时:

vector<T> func(){
    vector<T> result;
    ...
    return result; 
}

按以下方式使用:

vector<T> result = func(); 

为了避免复制我的容器的开销 我经常编写函数,以便它只返回接受一个 容器的非常量实例。

void func(vector<T>& result){
    result.clear();
    ...
    result;
}

按以下方式使用:

vector<T> result;
func(result); 

我的努力是否毫无意义,因为我可以确定编译器 总是使用返回值优化?

【问题讨论】:

  • 从 C++11 开始你的努力是没有意义的,直到 C++11 是正确的方法。
  • 即使在 C++03 中,您也应该使用您的第一个代码示例。
  • 您假设如果应用 RVO,它将与其他方式一样有效。情况并非总是如此。
  • @101010 即使在 C++11 之前,将其称为“正确的方法”也是有问题的……
  • @TheParamagneticCroissant 很公平,但在那个年代就是这样。

标签: c++ rvo


【解决方案1】:

没有意义。你提到的RVO类型叫做命名RVO(NRVO),大部分编译器都实现了。

不管怎样,在 C++11 中,vector 有移动构造函数,所以即使 NRVO 不适用,它仍然会被移动,而不是复制。

【讨论】:

    【解决方案2】:

    不保证 RVO,但体面的编译器会在允许的情况下使用它。

    但问题是 RVO 仅在您在函数外部创建新对象时才有帮助。如果您通过引用传递相同的向量来重用它,您可以利用它的保留容量来减少内存分配的数量。在函数内部创建的本地向量总是需要在内部分配一个新的缓冲区,无论返回值存储在哪里。因此,通过引用传递向量会更有效,即使代码看起来不太好。

    【讨论】:

      【解决方案3】:

      取决于您的编译器的年龄。在 C++11 之前,除非编译器支持命名返回值优化(并非所有较旧的编译器都支持),否则需要您的替代方法。此外,您还可以让函数返回对传递的向量的引用。

      从 C++11 开始,该语言支持移动构造,并且标准容器具有有效的移动构造器,因此您的第一种方法很好。纯粹主义者会坚持这样更好。实用主义者(他们意识到不是每个人都可以在没有巨额费用的情况下更新他们的编译器)会说根据您的代码是否需要继续使用 C++11 之前的编译器和更高版本的编译器来选择解决方案。

      【讨论】:

      • 即使使用较旧的编译器,您也可以体验一下它是否实现了复制省略。
      • 确实,M.M.或者阅读编译器文档。但是,仍然有代码需要与多个编译器(甚至是编译器版本和设置)、操作系统之间等一起工作。
      【解决方案4】:

      我已经用 gcc 试过了。我意识到在没有 C++11 标志的情况下编译时我不能依赖 NRVO。

      因为我不喜欢第二个签名(函数通过引用获取容器),所以我得出了这个结论:

      以自然形式声明函数:

      vector<T> func(){
          vector<T> result;
          ...
          return result; 
      }
      

      并且,当我不确定编译器和编译标志时,以这种方式使用它:

      vector<T> result;
      func().swap(result)
      

      通过这种方式可以获得所需的接口,并确保避免可忽略的开销。

      注意result 向量的容量是函数返回的向量之一。如果要设置向量的容量,则该函数的正确接口是第二个。

      【讨论】:

      • 交换和传递引用看起来都很难看。您不妨坚持参考方法。
      • IMO 交换优于参考方法,因为交换不涉及声明。该函数的签名更具可读性(在我的示例中,不返回向量)。
      • 参考方法可以利用现有的向量容量。在高性能应用程序中,有时您必须牺牲一些代码的美观性来换取速度。
      • @NeilKirk 我完全同意这一点,这就是为什么我写(在我自己的回答末尾)最后一段提到你在回答中提出的“容量问题”。跨度>
      猜你喜欢
      • 1970-01-01
      • 2010-12-30
      • 2021-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-14
      • 2023-03-08
      • 2012-11-22
      相关资源
      最近更新 更多