Operator Overloading

Syntax

Assumptions: a and b are operands; @ is the placeholder for the operator, not the @ operator

Operator Overloading Syntax

Expression

As method

As function

@a

(a).operator@()

operator@(a)

a@b

(a).operator@(b)

operator@(a, b)

  • Operators that can be overloaded

    + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- , ->* ->

Operator overloading as methods

  • Only when a is a class-typed object

  • Declare as a method in the class of the object a

  • Declare as const method to make a const

  • Declare b as a const formal parameter to make b

 1class Point {
 2  int x;
 3  int y;
 4 public:
 5  Point()=default;
 6  Point(int x, int y): x(x), y(y) {}
 7  Point operator+(const Point& other) const {
 8    return Point(x + other.x, y + other.y);
 9  }
10  // binary - operator
11  Point operator-(const Point& other) const {
12    return Point(x - other.x, y - other.y);
13  }
14  // Unary - operator
15  Point operator-() const {
16    return Point(-x, -y);
17  }
18  // better to make it const! Check the warning after
19  bool operator==(const Point& other) const {
20    return x == other.x && y == other.y;
21  }
22  void print() const {
23    cout << "X: " << x << " Y: " << y << endl;
24  }
25};
26
27int main() {
28  Point p1(10, 5), p2(2, 1);
29  Point p3 = p1 + p2;
30  Point p4 = p1 - p2;
31  p3.print();
32  (-p3).print();
33  p4.print();
34  if (p1 == p2) cout << "P1 equals P2" << endl;
35  else cout << "P1 not equal P2" << endl;
36  return EXIT_SUCCESS;
37}

Operator overloading as functions

  • a and b can be any type

  • Declare a and b as a const formal parameters to make them const

  • may be declared as a friend function of a class to access private instance variables

 1class Point {
 2  int x;
 3  int y;
 4 public:
 5  Point()=default;
 6  Point(int x, int y): x(x), y(y) {}
 7  void print() const {
 8    cout << "X: " << x << " Y: " << y << endl;
 9  }
10  // binary +
11  friend Point operator+(const Point& p1, const Point & p2) {
12    // will need public getter for x and y if not defined as a friend method
13    // return Point(p1.getX() + p2.getX(), p1.getY(), p2.getY()
14    return Point(p1.x + p2.x, p1.y + p2.y);
15  }
16  // binary -
17  friend Point operator-(const Point& p1, const Point & p2) {
18    return Point(p1.x - p2.x, p1.y - p2.y);
19  }
20  // Unary - operator
21  friend Point operator-(const Point &p1) {
22    return Point(-p1.x, -p1.y);
23  }
24  friend bool operator==(const Point& p1, const Point & p2) {
25    return p1.x == p2.x && p1.y == p2.y;
26  }
27};
28
29int main() {
30  Point p1(10, 5), p2(2, 1);
31  Point p3 = p1 + p2;
32  Point p4 = p1 - p2;
33  p3.print();
34  (-p3).print();
35  p4.print();
36  if (p1 == p2) cout << "P1 equals P2" << endl;
37  else cout << "P1 not equal P2" << endl;
38  return EXIT_SUCCESS;
39}

Const

It is a great practice to make everything const if possible. For instance, operators like arithmetic, relational, logical operators can usually be made const. Some operators like ++/--, =, +=, etc. cannot be const as they must modify the internal state of the object.

Warning

== operator used in Catch2 tests with REQUIRE or CHECK macros must be const. Unless it is protected with extra pair of parenthesis.

CHECK(p1 == p2);  // requires == operator to be const
CHECK((p1 == p2));  // does not require == operator to be const

Best Practice

Do not abuse operator overloading. Only use it when it is logically making sense. Anything that may surprise the user should be avoided.