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.
> Note also that C doesn't have return-value-optimization, hence all your struct-returning functions will cause a call to memcpy (won't happen when compiled in C++ mode of course).
In the context of the C++ programming language, return value optimization (RVO) is a compiler optimization that involves eliminating the temporary object created to hold a function's return value. RVO is allowed to change the observable behaviour of the resulting program by the C++ standard.
(Named) Return value optimization is a common form of copy elision. It refers to the situation where an object returned by value from a method has its copy elided. The example set forth in the standard illustrates named return value optimization, since the object is named.
Compilers often perform Named Return Value Optimization (NRVO) in such cases, but it is not guaranteed.
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.
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With