C++ return value optimization

生来就可爱ヽ(ⅴ<●) 提交于 2019-11-28 07:17:44

问题


This code:

#include <vector>

std::vector<float> getstdvec() {
    std::vector<float> v(4);

    v[0] = 1;
    v[1] = 2;
    v[2] = 3;
    v[3] = 4;

    return v;
}

int main() {
    std::vector<float> v(4);

    for (int i = 0; i != 1000; ++i)
    {
        v = getstdvec();
    }
}

My incorrect understanding here is that the function getstdvec shouldn't have to actually allocate the vector that it's returning. When I run this in valgrind/callgrind, I see there are 1001 calls to malloc; 1 for the initial vector declaration in main, and 1000 for every loop iteration.

What gives? How can I return a vector (or any other object) from a function like this without having to allocate it every time?

edit: I'm aware I can just pass the vector by reference. I was under the impression that it was possible (and even preferable) to write a function like this that returns an object without incurring an unnecessary allocation.


回答1:


When you call a function, for a return type like std::vector<T> the compiler provides memory for the returned object. The called function is responsible for constructing the instance it returns in this memory slot.

The RVO/NRVO now allows the compiler to omit creating a local temporary object, copy-constructing the returned value in the memory slot from it, destructing the temporary object and finally returning to the caller. Instead, the called function simply constructs the local object in the return slot's memory directly and at the end of the function, it just returns.

From the caller's perspective, this is transparent: It provides memory for the returned value and when the function called returned, there is a valid instance. The caller may now use this object and is responsible for calling the destructor and freeing the memory later on.

This means that the RVO/NRVO only work for when you call a function to construct a new instance, not when you assign it. The following is an example of where RVO/NRVO could be applied:

std::vector<float> v = getstdvec();

but you original code uses a loop and in each iteration, the result from getstdvec() needs to be constructed and this temporary is assigned to v. There is no way that the RVO/NRVO could remove this.




回答2:


You can pass it by reference...copy elision makes it so that v = getstdvect() allocates v (in your main) directly to the v (in your getstdvec()) and skips the copy usually associated with returning by value, but it will NOT skip the v(4) in your function. In order to do that, you need to take the vector in by reference:

#include <vector>
void getstdvec(std::vector<float>& v){
  v.resize(4);//will only realocate if v is wrong size
  v[0] = 1; v[1] = 2; v[2] = 3; v[3] = 4;
  return v;
}
int main() {
  std::vector<float> v(4);
  for (int i=0; i!=1000;++i)
    getstdvec(v);
}



回答3:


You're doing copy-assignment in your loop, not copy-construction. The RVO optimization only applies to constructing variables from a return value, not assigning to them.

I can't quite make out the real problem you're trying to solve here. With more details it might be possible to provide a good answer that addresses your underlying problem.

As it stands, to return from your function in such a way you'll need to create a temporary vector to return each time the function is called.




回答4:


The simplest answer is to pass the already created vector object into the function.

std::vector<float> getstdvec(std::vector<float> &myvec){

In that case you don't really have to return it so

void getstdvec(std::vector<float> &myvec){



回答5:


in stead use the return value, you can use a reference:

void getstdvec(std::vector<float> &v)

Which can avoid the copy of temporary object




回答6:


How can I return a vector (or any other object) from a function like this without having to allocate it every time?

In your way, you declare a local vector with size 4, so each time the function is called, it is going to allocate the memory. If you means that you always modify on the same vector, then you may consider pass the vector by reference instead.

For example:

void getstdvec(std::vector<float>& vec)
{                                //^^
    //do something with vec
}

inside main, you declare the vector and allocate space as what you did. You now do the following:

for (int i=0; i!=1000;++i)
{        //^^^minor: Don't use magic number in the code like this, 
         //define a const instead
    getstdvec(vec);
}


来源:https://stackoverflow.com/questions/19454068/c-return-value-optimization

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!