Hi, what happens at the return of this piece of code?
1 2 3 4 5 6 7 8 9 10 11 12
vector<int> MyFunction(...){
vector<int> vect;
//do something to populate the vector here
while(..){
vect.push_back(something);
}
return vect;
}
//Calling my MyFunction
auto returned_vect = MyFunction(...)
At the return ,is there a copy of vect to returned_vect? If there is, how to avoid the copy?
A language expert can probably give a better description, but that article above explains it in depth.
I think in this case, it's covered by
Non-mandatory elision of copy/move (since C++11) operations
...
In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO, "named return value optimization".
This NRVO is a compiler optimization and not mandatory. But I might be wrong, and I encourage any one else to also reply.
Studying the assembly produced can also help demystify things.
You can also do things like make the constructor have side-effects...
Just my 2 cents worth, I don't wish to argue per say: you sound way more experienced/ knowledgeable than me, a back yard basher :+) Welcome to the forum btw, I have enjoyed reading your posts so far :+)
If not using copy elision, I would much prefer returning a reference using the out parameter as in your first code snippet.
Using new with a vector which already puts it's data on the heap, I am not so much of a fan. One reason is that std::vector is a RAII container: using new kind of negates that.
In the past I have said that C++ compilers seem to implement references as const pointers wrapped up in some TMP, no one disagreed then, that might mean the statement was correct. I am yet to see an example of how to do it otherwise. I am aware that the C++ standard does not specify how to implement things, that is left up to the vendors. So using a reference and a pointer as per these two examples might be the same thing.
I wonder which compilers don't implement NRVO, it seems that g++ and clang++ do; I am not sure* that I can test the MS one, I am a Linux guy :+) * Not sure if it's possible to install MS compiler into Visual Studio Code on Linux?
The other thing with this is it depends on which standard one is compiling against. I thought I would mention that, because I always use the latest versions of everything, I sometimes forget about the poor souls still stuck with c++14, or heaven forbid c++11 :+|
The Microsoft compiler does implement non-mandatory copy elision in release builds;
in debug builds, only mandatory copy elision is performed.
Mainstream implementations consistently perform this optimisation where it is permitted;
to the extent that a warning may be generated if it is disabled by a programming construct.
1 2 3 4 5 6 7 8
std::vector<int> baz()
{
std::vector<int> vec { 0, 1, 2, 3, 4, 5, 6 } ;
return std::move(vec) ; // copy elision is disabled
// *** warning *** : moving a local object in a return statement prevents copy elision
// note: remove std::move call here
}
Note that passing a vector by reference to try and avoid copy construction (first default construct and then assign to it) tends to be sub-optimal, at least post C++11.
Thanks for the link. I think it makes sense that NRVO is doable and not mandatory.
I think you're suggesting that this code printing 5 dots means there's no copy.
However, printing 5 dots just means that 5 Thing objects were created during the push_back.
Then it copies the 5 objects to the container vect.
Whether there's a copy at the return is still unknown.
If you're worried about a copy happening on the function return value for say a vector, then in the function display the .data() value and also for the returned value from the function.
vector<int>& MyFunction(...) {
vector<int> *vect = new vector<int>();
//do something to populate the vector here
while(..){
vect->push_back(something);
}
return *vect;
}
Then when/how is the object deleted/destroyed?
That is exactly the problem with this "solution". There would be no straight-forward way to destroy (or even know that you are supposed to destroy) the heap allocated object for the caller of your function!
In theory, the caller could use the & operator to get a pointer from the reference, just like you can get a pointer from a "local" object, and then it can be delete'd. But that "workaround" has a smell to me :-[
Yeah, it's a bit annoying when I have to declare vector<int> returned_vect;
before calling the function
But it's extremely common to have functions that take a reference (or pointer (or iterator)) to an existing container object and then perform some kind of operation on that existing container.
After all, I think you can just return an std:vector object by value and let [N]RVO do its job ;-)
> The addresses of internal Thing and returned Thing are different.
> What exactly should be done at the return to ensure mandatory copy elison?
To see the effect of copy elision, return the original local object; not a second one (line #47).
This is non-mandatory copy elision (copy elision that is permitted, but not required by the standard);
all mainstream implementations do perform this optimisation.
To see copy elision in action, run this (modified) code: