Operator Overloading  «Prev  Next»
Lesson 6 The operator keyword
Objective Learn how to use the operator keyword to overload operators in C++

Using the operator Keyword in C++

Operator overloading in C++ lets you redefine how operators behave for your own classes, providing more natural syntax for custom types. The mechanism is implemented through the operator keyword. However, it is critical to follow best practices: only overload operators when it improves clarity, preserve expected semantics, and prefer return-by-reference when modifying objects.

Basic Operator Overloading

Overloading an operator is similar to writing a function, except the function name is formed with the keyword operator followed by the operator symbol:


class Foo {
public:
    Foo operator-();        // unary minus
    Foo operator-(int);     // binary minus Foo - int
    Foo operator-(const Foo&); // binary minus Foo - Foo
};

// non-member overload
Foo operator-(int, const Foo&);  // int - Foo (legal)
// Foo operator-(int, Foo*);     // illegal: must take Foo or Foo&

Operators That Can Be Overloaded

Figure 3-6: Operators that can be overloaded

+   -   *   /   =   <   >   +=   -=   *=   /=   <<   >>
<<= >>= ==  !=   <=  >=  ++   --   %    &    ^    !   |
~   &=  ^=  |=   &&  ||   %=   []   ()   ,    ->*  ->
new delete new[] delete[]
Figure 3-6: Operators that can be overloaded

Operators That Cannot Be Overloaded

Operators that cannot be overloaded
. .* :: ?:

Precedence, Associativity, and Operands

Operator overloading cannot change the built-in precedence, associativity, or the number of operands:

This means parentheses may be required for clarity when mixing overloaded and built-in operators.

Conversion Operators

Operator overloading can also define implicit type conversions. A class may declare a conversion operator that allows objects to be treated as another type:


class Three {
    int i;
public:
    explicit Three(int ii = 0) : i(ii) {}
};

class Four {
    int x;
public:
    Four(int xx) : x(xx) {}
    operator Three() const { return Three(x); } // conversion operator
};

void g(Three) {}

int main() {
    Four four(1);
    g(four);  // converted via operator Three()
    g(1);     // calls Three(int) constructor
}

Here, Four defines an operator conversion to Three. This allows seamless use of Four in contexts expecting Three. Unlike constructors, which perform conversion in the destination type, operator conversions are defined in the source type. To avoid unintended implicit conversions, mark single-argument constructors and conversion operators with explicit.

Best Practices

  1. Overload only operators where the meaning is natural and intuitive for your type.
  2. Preserve expected semantics (e.g., operator+ should not modify operands).
  3. Use member functions for operators that modify the current object; prefer non-member functions for symmetric binary operators.
  4. Return by reference where appropriate to avoid unnecessary copies.
  5. Avoid gratuitous overloads that reduce clarity.

SEMrush Software