Question

I want freeFunct to do non const stuff on its own copy of object a.

Let's say that freeFunct is required to be a free function because in real code cases it takes many different parameters, calls several public functions from all of them and there is no point in making it a non-static member function of any class.

Three different ways of declaring it come to my mind.

I have the feeling that the third solution is the worse.

Is there any difference between the first two?

Is there something better?

void freeFunct1(A a){
    a.doStuff(); 
}

void freeFunct2(const A& a){
    A b = a; 
    b.doStuff(); 
}

/**users of freeFunct3 are expected 
 *to give a copy of their variable: 
 *{
 *    A b = a; 
 *    freeFunct3(b); 
 *}
 */
void freeFunct3(A& a){
    a.doStuff(); 
}
Was it helpful?

Solution 2

First, as already said, don't do freeFunct3 if the semantics of the free function is to only modify its "own" object.

Second, there are differences between freeFunct1 and freeFunct2, relating to move optimization [C++11], exception safety, and potentially code size.

With freeFunct2 (taking by reference-to-const):

  1. It will always construct a new copy of the argument, never move it [C++11].
  2. If the copy construction of A throws an exception, it will throw inside the body of the function.
  3. If A's copy constructor is inlined (and the function is not), it will be expanded once, inside the body of the function (even if the function is called from multiple different places).

With freeFunct1 (taking by value):

  1. [C++11] You can avoid a copy if A has a move constructor and you pass an rvalue (e.g. call freeFunct1(A(args))).
  2. If the copy (or move) construction of A throws an exception, it will throw at the call site.
  3. If A's copy (or move) constructor is inlined, it will be expanded multiple times, at each call site.

Alternatively, you can overload on lvalue/rvalue reference to avoid unnecessarily copying rvalues:

void freeFunct4(const A& a){
    A b = a; 
    b.doStuff(); 
}
void freeFunct4(A&& a){
    a.doStuff(); 
}

OTHER TIPS

The first is best: it allows the caller to choose whether to copy or move his object, so can be more efficient if the caller doesn't need to keep a copy.

freeFunct1(a);             // "a" is copied and not changed
freeFunct1(std::move(a));  // "a" is moved and perhaps changed

The second is similar, but forces a copy.

The third, as you say, is more error-prone, since the caller has to be aware that it will modify the argument.

IMO, the first is the best and the last is the worst.

Quite a few people, however, have gotten so accustomed to passing by const reference that they'll write #2 by default, even though in this case they need the copy that it's trying to avoid.

The first changes only the local copy. The second is the same as the first, but with extra code. The third will make changes to a visible to the caller of freeFunct3 as it's a non-const reference. If called as in the comment above the function, then it's no different than the second version really.

So if you just want to modify the local copy, without those changes being passed to the caller, then the first version is what I recommend.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top