views:

704

answers:

7

I had assumed that the canonical form for operator+, assuming the existence of an overloaded operator+= member function, was like this:

const T operator+(const T& lhs, const T& rhs)
{
    return T(lhs) +=rhs;
}

But it was pointed out to me that this would also work:

const T operator+ (T lhs, const T& rhs)
{
    return lhs+=rhs;
}

In essence, this form transfers creation of the temporary from the body of the implementation to the function call.

It seems a little awkward to have different types for the two parameters, but is there anything wrong with the second form? Is there a reason to prefer one over the other?

+1  A: 

With the edited question, the first form would be preferred. The compiler will more likely optimize the return value (you could verify this by placing a breakpoint in the constructor for T). The first form also takes both parameters as const, which would be more desirable.

Research on the topic of return value optimization, such as this link as a quick example: http://www.cs.cmu.edu/~gilpin/c++/performance.html

Kris Kumler
So does the second form.
Corey Ross
Not necessarily. Most modern compilers will detect that only one copy is necessary and optimize it away.
Jesse Beder
(The above is intended @Kris, but actually applies to @Corey also.)
Jesse Beder
Corey: See Scott Meyer's More Effective C++, Item 20.
Kris Kumler
@Kris K. I will look that up when I get home.
Corey Ross
I've worked with compilers recently that, when they did support the optimization, you had to make sure that it was turned on.
Kris Kumler
RVO, right on. There is also (named) NRVO which should make both cases have very similar generated code (verify with your compiler!). http://blogs.msdn.com/slippman/archive/2004/02/03/66739.aspxhttp://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspxI think this is what Jesse is getting at.
Corey Ross
I fixed the fist version to increase the likelihood that RVO would work.
JohnMcG
+4  A: 

I'm not sure if there is much difference in the generated code for either.

Between these two, I would (personally) prefer the first form since it better conveys the intention. This is with respect to both your reuse of the += operator and the idiom of passing templatized types by const&.

Corey Ross
+1  A: 

I would prefer the first form for readability.

I had to think twice before I saw that the first parameter was being copied in. I was not expecting that. Therefore as both versions are probably just as efficient I would pick them one that is easier to read.

Martin York
Ditto. I was staring at the second example wondering how it could possibly be equivalent, and then I saw that it wasn't being passed by reference. The first version is much more clear.
Derek Park
+1  A: 
const T operator+(const T& lhs, const T& rhs)
{
    return T(lhs)+=rhs;
}

why not this if you want the terseness?

Keith Nicholas
Thanks -- and this is what's in the Myers book to eliminate the temporary -- I've adjusted it.
JohnMcG
+1  A: 

My first thought is that the second version might be infinitessimally faster than the first, because no reference is pushed on the stack as an argument. However, this would be very compiler-dependant, and depends for instance on whether the compiler performs Named Return Value Optimization or not.

Anyway, in case of any doubt, never choose for a very small performance gain that might not even exist and you more than likely won't need -- choose the clearest version, which is the first.

Jan de Vos
A: 

Actually, the second is preferred. As stated in the c++ standard,

3.7.2/2: Automatic storage duration

If a named automatic object has initialization or a destructor with side effects, it shall not be destroyed before the end of its block, nor shall it be eliminated as an optimization even if it appears to be unused, except that a class object or its copy may be eliminated as specified in 12.8.

That is, because an unnamed temporary object is created using a copy constructor, the compiler may not use the return value optimization. For the second case, however, the unnamed return value optimization is allowed. Note that if your compiler implements named return value optimization, the best code is

const T operator+(const T& lhs, const T& rhs)
{
    T temp(lhs);
    temp +=rhs;
    return temp;
}
A: 

I think that if you inlined them both (I would since they're just forwarding functions, and presumably the operator+=() function is out-of-line), you'd get near indistinguishable code generation. That said, the first is more canonical. The second version is needlessly "cute".

Drew Hall
Whoa--didn't realize how old this question was--hope you're still interested! :)
Drew Hall