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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10476665/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:07:19  来源:igfitidea点击:

Avoiding copy of objects with the "return" statement

c++objectcopyreturnreturn-value

提问by Vincent

I have a very basic question in C++. How to avoid copy when returning an object ?

我在 C++ 中有一个非常基本的问题。返回对象时如何避免复制?

Here is an example :

这是一个例子:

std::vector<unsigned int> test(const unsigned int n)
{
    std::vector<unsigned int> x;
    for (unsigned int i = 0; i < n; ++i) {
        x.push_back(i);
    }
    return x;
}

As I understand how C++ works, this function will create 2 vectors : the local one (x), and the copy of x which will be returned. Is there a way to avoid the copy ? (and I don't want to return a pointer to an object, but the object itself)

据我了解 C++ 的工作原理,此函数将创建 2 个向量:本地向量 (x) 和将返回的 x 副本。有没有办法避免复制?(我不想返回指向对象的指针,而是返回对象本身)



What would be the syntax of that function using "move semantics" (which was stated in the comments)?

使用“移动语义”(在评论中说明)的函数的语法是什么?

采纳答案by Pubby

This program can take advantage of named return value optimization (NRVO). See here: http://en.wikipedia.org/wiki/Copy_elision

该程序可以利用命名返回值优化 (NRVO)。见这里:http: //en.wikipedia.org/wiki/Copy_elision

In C++11 there are move constructors and assignment which are also cheap. You can read a tutorial here: http://thbecker.net/articles/rvalue_references/section_01.html

在 C++11 中有移动构造函数和赋值,它们也很便宜。您可以在这里阅读教程:http: //thbecker.net/articles/rvalue_references/section_01.html

回答by Matthieu M.

There seems to be some confusion as to how the RVO (Return Value Optimization) works.

关于 RVO(返回值优化)的工作方式似乎有些混乱。

A simple example:

一个简单的例子:

#include <iostream>

struct A {
    int a;
    int b;
    int c;
    int d;
};

A create(int i) {
    A a = {i, i+1, i+2, i+3 };
    std::cout << &a << "\n";
    return a;
}

int main(int argc, char*[]) {
    A a = create(argc);
    std::cout << &a << "\n";
}

And its output at ideone:

它在ideone 的输出:

0xbf928684
0xbf928684

Surprising ?

令人惊讶?

Actually, that is the effect of RVO: the object to be returnedis constructed directly in placein the caller.

实际上,这就是 RVO 的效果:要返回的对象直接调用者中就地构造。

How ?

如何 ?

Traditionally, the caller (mainhere) will reserve some space on the stack for the return value: the return slot; the callee (createhere) is passed (somehow) the address of the return slot to copy its return value into. The callee then allocate its own space for the local variable in which it builds the result, like for any other local variable, and then copies it into the return slot upon the returnstatement.

传统上,调用者(main此处)会在堆栈上为返回值保留一些空间:返回槽;被调用者(create此处)被传递(以某种方式)返回槽的地址以将其返回值复制到其中。然后被调用者为它构建结果的局部变量分配自己的空间,就像任何其他局部变量一样,然后将其复制到return语句的返回槽中。

RVO is triggered when the compiler deduces from the code that the variable can be constructed directly into the return slotwith equivalent semantics (the as-if rule).

当编译器从代码中推断出该变量可以直接构造到具有等效语义的返回槽中时(as-if 规则),就会触发 RVO 。

Note that this is such a common optimization that it is explicitly white-listed by the Standard and the compiler does not have to worry about possible side-effects of the copy (or move) constructor.

请注意,这是一种常见的优化,它被标准明确列入白名单,编译器不必担心复制(或移动)构造函数可能产生的副作用。

When ?

什么时候 ?

The compiler is most likely to use simple rules, such as:

编译器最有可能使用简单的规则,例如:

// 1. works
A unnamed() { return {1, 2, 3, 4}; }

// 2. works
A unique_named() {
    A a = {1, 2, 3, 4};
    return a;
}

// 3. works
A mixed_unnamed_named(bool b) {
    if (b) { return {1, 2, 3, 4}; }

    A a = {1, 2, 3, 4};
    return a;
}

// 4. does not work
A mixed_named_unnamed(bool b) {
    A a = {1, 2, 3, 4};

    if (b) { return {4, 3, 2, 1}; }

    return a;
}

In the latter case (4), the optimization cannot be applied when Ais returned because the compiler cannot build ain the return slot, as it may need it for something else (depending on the boolean condition b).

在后一种情况 (4) 中,无法在A返回时应用优化,因为编译器无法a在返回槽中构建,因为它可能需要它用于其他用途(取决于布尔条件b)。

A simple rule of thumb is thus that:

因此,一个简单的经验法则是:

RVO should be applied if no other candidate for the return slot has been declared prior to the returnstatement.

如果在声明之前没有声明返回位置的其他候选者,则应应用 RVO return

回答by LihO

Named Return Value Optimizationwill do the job for you since the compiler tries to eliminate redundant Copy constructor and Destructor callswhile using it.

命名返回值优化将为您完成这项工作,因为编译器在使用它时会尝试消除冗余的复制构造函数和析构函数调用

std::vector<unsigned int> test(const unsigned int n){
    std::vector<unsigned int> x;
    return x;
}
...
std::vector<unsigned int> y;
y = test(10);

with return value optimization:

返回值优化:

  1. y is created
  2. x is created
  3. x is assigned into y
  4. x is destructed
  1. y 已创建
  2. x 已创建
  3. x 赋值给 y
  4. x 被破坏

(in case you want to try it yourself for deeper understanding, look at this example of mine)

(如果您想自己尝试以加深理解,请看我的这个例子

or even better, just like Matthieu M.pointed out, if you call testwithin the same line where yis declared, you can also avoid construction of redundant object and redundant assignment as well (xwill be constructed within memory where ywill be stored):

甚至更好,就像Matthieu M.指出的那样,如果您testy声明的同一行内调用,您还可以避免构造冗余对象和冗余分配(x将在y将存储的内存中构造):

std::vector<unsigned int> y = test(10);

check his answer for better understanding of that situation (you will also find out that this kind of optimization can not always be applied).

检查他的回答以更好地了解这种情况(您还会发现这种优化并不总是适用)。

ORyou could modify your code to pass the reference of vector to your function, which would be semantically more correct while avoiding copying:

或者您可以修改您的代码以将 vector 的引用传递给您的函数,这在避免复制的同时在语义上更正确:

void test(std::vector<unsigned int>& x){
    // use x.size() instead of n
    // do something with x...
}
...
std::vector<unsigned int> y;
test(y);

回答by jamesdlin

Compilers often can optimize away the extra copy for you (this is known as return value optimization). See https://isocpp.org/wiki/faq/ctors#return-by-value-optimization

编译器通常可以为您优化掉额外的副本(这称为返回值优化)。请参阅https://isocpp.org/wiki/faq/ctors#return-by-value-optimization

回答by Jake Runzer

Referencing it would work.

引用它会起作用。

Void(vector<> &x) {

}

回答by Dr. Aleksandr Yampolskiy

First of all, you could declare your return type to be std::vector & in which case a reference will be returned instead of a copy.

首先,您可以将返回类型声明为 std::vector & 在这种情况下将返回引用而不是副本。

You could also define a pointer, build a pointer inside your method body and then return that pointer (or a copy of that pointer to be correct).

您还可以定义一个指针,在您的方法体内构建一个指针,然后返回该指针(或该指针的正确副本)。

Finally, many C++ compilers may do return value optimization (http://en.wikipedia.org/wiki/Return_value_optimization) eliminating the temporary object in some cases.

最后,在某些情况下,许多 C++ 编译器可能会执行返回值优化 (http://en.wikipedia.org/wiki/Return_value_optimization) 以消除临时对象。