Binary Operator Overloading

In C++, there are two forms of binary operator overloading you can use when designing an API. The first form is to overload the operator as a member function of the class, and the second form is to overload the operator as a friend function of the class. I want to explore why you would use one form of overloading instead of the other, using a Fraction class as an example.

For the purposes of this discussion, this is part of the interface for our expository class.

class Fraction {
  // Implementation details live here.

public:
  Fraction(int Whole);
  Fraction(int Numerator, unsigned Demoninator);
  Fraction(double Value);

  // Binary operator overloads live here.
};

One of the ways we can implement our binary operator overloads is as member functions of the Fraction class. I’m going to pick on the equality operator, but any of the overloaded binary operators would suffice.

  ...
  // Binary operator overloads live here.
  bool operator==(const Fraction &RHS) const;
  ...

The other way we can implement our binary operator overloads is as a friend function of the Fraction class.

  ...
  // Binary operator overloads live here.
  friend bool operator==(const Fraction &LHS, const Fraction &RHS);

Since there are two different ways to implement this, it’s reasonable to ask which way is “correct?” The answer to that question depends on your intentions as a class designer. Consider the following use case:

void f(const Fraction &F) {
  if (1.0 == F) {
    // Do something interesting
  }
}

Some coding conventions suggest that equality comparisons against a constant value put the constant on the left-hand side of the comparison (so that an accidental assignment operation by typing = instead of == would trigger a compile error), so this example is not particularly far-fetched.

If you use a member function for the operator overload, this code would not compile because there’s no way for the implicit converting constructor from double to Fraction to be called. However, by using a friend function for the operator overload, the compiler can call the converting constructor to create a Fraction object which would make the comparison viable. Because of this, I would claim that declaring the operators to be friends is the correct approach for the class design.

This exemplifies a reasonable way to decide how to implement the overloaded binary operators. If you want to allow implicit conversions for items on the left-hand side of the operator, then using friend function overloads is required. If implicit conversions are not desirable for some reason, or not possible (due to having no implicit converting constructors), then using a member function is acceptable. If you’re looking for a general rule of thumb, I would recommend always using the friend function form — it’s more likely to behave how the user would expect in all cases, instead of having curious edge cases where their usage fails. Imagine how confusing it would be for the user of a Fraction class that SomeFraction * 1 succeeds, but 1 * SomeFraction fails to compile! That being said, it ultimately boils down to a design choice that you must make as a class designer.

I would like to thank Jens Maurer for the design discussion which spawned this blog posting.

This entry was posted in C/C++, Framework Design and tagged , . Bookmark the permalink.

2 Responses to Binary Operator Overloading

  1. Dan says:

    Aaron –

    Nice to see a new post. Just the other day I was going through my RSS feeds, removing feeds that hadn’t had a post in a year or so, and I saw “Ruminations” (obviously I didn’t remove the feed!)

    I always find your topics interesting and useful to my work. Thanks for taking the time to share the knowledge.

  2. Aaron Ballman says:

    @Dan — my pleasure! It’s been a long while since I’ve posted regularly, but I’m trying to get in the habit of at least posting occasionally. I’m glad you’ve not removed me from your RSS feed yet. :-)

Leave a Reply

Your email address will not be published. Required fields are marked *