This post is part of a series of tutorials on C++

Unlike the previous post on arithmetic operators, there is a more canonical form you might want to follow. While it is considered best practice for C++, it can sometimes encounter performance penalities because of the pass-by-copy and return-by-copy approach (I found this version slowed down my ray tracer significantly).

The idea relies on a couple of principles:

  • If you code a given operator #, then you should code #=. For example, if you code operator+() then you should code operator+=(). This is almost always a good idea and you should do this anyway (it’s rule 3 of the c++ - Three Basic Rules of Operator Overloading (stackoverflow.com).

  • But, if you bother to code one version, you might want to take advantage of code reuse (not always the best choice, but it does help prevent code bugs).

Taken together, this forms the following canonical form:

class Foo
{
 public:
  Foo& operator+=(const Foo& rhs) {                           
    //add rhs to this
    return *this;     //return by reference, similar to assignment operator
  }
 
  friend Foo operator+(Foo lhs,        // passing lhs by value 
                       const Foo& rhs) {
    lhs += rhs;       //reuse compound assignment
    return lhs;   
  }
};

This is kind of a neat way to code things up. You only have to implement addition once (in the compound operator+=(). You then can use the friend function to define the binary arithmetic operator+(). In this case, the friend function is both declare and defined within the class, with allows the compiler to do some optimizations (the function is automatically inlined).

But, you can pay a penalty here if you’re not carefuly. The problem is that lhs is passed-by-value, which calls a copy constructor once. And after the +=, you then return another copy of lhs by value, creating another copy call. This can be suboptimal if you have no created the right assignment operators to handle this gracefully. In particular, this requires a properly coded move assignment (cppreference.com), which can be a handy thing to have if you class has no dynamically allocated heap memory. You may also find my discussion on Assignment Operators, Part 2 to be interesting to consider in this context.

Still, for the most basic use cases, the important takeaway is that if you’re going to code a binary arithmetic operator, you should also code the compound one. And, if you can reuse code between the two, try to!



Back to C++ Tutorials