我返回时如何避免复制

Posted

技术标签:

【中文标题】我返回时如何避免复制【英文标题】:How to avoid the copy when I return 【发布时间】:2013-08-09 23:21:52 【问题描述】:

我有一个返回向量或集合的函数:

set<int> foo() 
    set<int> bar;
    // create and massage bar
    return bar;


set<int> afoo = foo();

在这种情况下,我在函数 foo() 中创建了一个临时内存空间,然后 通过复制将其分配给afoo。我真的很想避免这个副本,任何简单的方法我 可以在 C++11 中做到这一点吗?我认为这与右值有关。

好的,更新问题:如果我要返回自己定义的对象, 不是向量或集合,这是否意味着我应该定义一个移动构造函数? 像这样:

class value_to_return 
  value_to_return (value_to_return && other) 
    // how to write it here? I think std::move is supposed to be used?
  

谢谢!!!

【问题讨论】:

您现在所拥有的不会在 C++11 中复制任何内容。它可能会采取行动,但即使是那些也几乎肯定会被 N/RVO 优化掉。 “当” - 使用不超过 10 年前发布的编译器进行编译。 @MooingDuck:在我工作的地方,“C++”的意思是“C++03”,最早在 2020 年之前不太可能改变。我怀疑 90% 以上的专业 C++ 程序员也是如此。 (大型组织雇用他们中的大多数,而大型组织的行动非常缓慢。)事实上,C++11 很有可能成为“C++ 的 C99” @Nemo:我认为你夸大了没有 C++11 的组织的数量。即使您不是,官方 C++ 也值得提及,即使答案还提供了 C++03 的详细信息。 @Lightness:这个问题专门讨论了 C++11。我检查了编辑历史,没有证据表明它没有专门询问 C++。所以是的,任何没有提到右值引用的答案都是不足的。 【参考方案1】:

Modem C++ 编译器将实现:给定类型T

如果 T 具有可访问的副本或移动构造函数,编译器可能 选择省略副本。这就是所谓的(命名的)return value optimization (RVO),它甚至在 C++11 之前就已指定,并且是 大多数编译器都支持。 否则,如果 T 有 move constructor,则移动 T(C++11 起)。 否则,如果 T 有复制构造函数,则复制 T。 否则,会发出编译时错误。

【讨论】:

如果我错了请纠正我,但 AFAIK 返回值优化仅在您直接在 return 语句中构造对象时才有效,如下所示:return (set&lt;int&gt;)(...); 智能编译器可能会转换问题中的代码进入这种形式,但对于更复杂的功能,我想它不会起作用。 @cmaster,在 OP 的情况下,RVO 也将适用。阅读我关于 RVO 的回答中的 wiki 链接。 :) @cmaster:在返回命名变量的情况下优化副本通常称为 NRVO,命名返回值优化,并且在多年前已被大多数编译器实现。 @JohannesD:很高兴听到这个消息,但在某些情况下它仍然是不可能的,不是吗?即,您仍然需要考虑以确保副本真正优化掉?【参考方案2】:

查看return value optimization。现代编译器会优化这种情况,在这种简单的情况下,不会在任何主要编译器上进行复制。

原则上,您也可以在函数之外创建对象,然后调用函数并将对象通过引用传递给它。那将是避免复制的旧方法,但现在是不必要且不可取的。

【讨论】:

【参考方案3】:

我通常通过将函数签名作为解决此问题

void foo(set<int> *x)

只需通过引用传递它,否则评论中已经提到了其他选项。

编辑:我已更改参数类型以说明 x 可以更改。

      set<int> s;
      foo(&s);

只有当你有一个旧的编译器时,这才是首选。我想某些项目可能就是这种情况。

而且,更好的做法是在 c++11 中使用移动语义。或者继续返回容器并在现代编译器中查看 RVO。

【讨论】:

我对此投赞成票,因为它是 only 建议传入输出参数的答案。一个完美的答案将包括 RVO 和移动构造函数。 几乎想投票 否决 提议输出参数。 @DeadMG 为什么输出参数这么可怕? 在许多情况下使用输出参数在现代编译器上的效率低于从函数返回结果。它还可能使您的代码可读性较差,因为在许多情况下,它会迫使您使用临时变量,而返回的值可以很容易地传递给其他函数或直接在表达式中使用。 接受输出参数的函数是不可组合的,而且它们也不太灵活。如果明天,你想要一个 unordered_set,我必须更改我所有的调用代码。如果我有auto set = foo(); 我什么都不会改变。输出参数只是模糊的。如果您想返回多个值,请使用元组,这就是它的用途。它也将是编译器所实现的按值返回。尤其是因为移动语义,整个 shebang 是不必要的。

以上是关于我返回时如何避免复制的主要内容,如果未能解决你的问题,请参考以下文章

使用支持对象/结构的泛型时如何返回空值/默认值? [复制]

不同DBMS之间复制时如何避免数据冗余?

避免使用“return”语句复制对象

如何在修改其副本时避免更新原始数组? [复制]

在 Javascript 中,当执行深度复制时,由于属性是“this”,我如何避免循环?

更新记录时如何避免mysql中的这个错误? [复制]