Let's say I want to implement a function that is supposed to process an object and return a new (possibly changed) object. I would like to do this as efficient as possible in C+11. The environment is as follows:
class Object {
/* Implementation of Object */
Object & makeChanges();
};
The alternatives that come to my mind are:
// First alternative:
Object process1(Object arg) { return arg.makeChanges(); }
// Second alternative:
Object process2(Object const & arg) { return Object(arg).makeChanges(); }
Object process2(Object && arg) { return std::move(arg.makeChanges()); }
// Third alternative:
Object process3(Object const & arg) {
Object retObj = arg; retObj.makeChanges(); return retObj;
}
Object process3(Object && arg) { std::move(return arg.makeChanges()); }
Note: I would like to use a wrapping function like process()
because it will do some other work and I would like to have as much code reuse as possible.
Updates:
I used the makeChanges()
with the given signature because the objects I am dealing with provides methods with that type of signature. I guess they used that for method chaining. I also fixed the two syntax errors mentioned. Thanks for pointing those out. I also added a third alternative and I will repose the question below.
Trying these out with clang [i.e. Object obj2 = process(obj);
] results in the following:
First option makes two calls to the copy constructor; one for passing the argument and one for returning. One could instead say return std::move(..)
and have one call to the copy constructor and one call to the move constructor. I understand that RVO can not get rid of one of these calls because we are dealing with the function parameter.
In the second option, we still have two calls to the copy constructor. Here we make one explicit call and one is made while returning. I was expecting for RVO to kick in and get rid of the latter since the object we are returning is a different object than the argument. However, it did not happen.
In the third option we have only one call to the copy constructor and that is the explicit one. (N)RVO eliminates the copy constructor call we would do for returning.
My questions are the following:
- (answered) Why does RVO kick in the last option and not the second?
- Is there a better way to do this?
- Had we passed in a temporary, 2nd and 3rd options would call a move constructor while returning. Is is possible to eliminate that using (N)RVO?
Thanks!