9.1 — Introduction to operator overloading

In lesson 7.6 -- Function overloading, you learned about function overloading, which provides a mechanism to create and resolve function calls to multiple functions with the same name, so long as each function has a unique function prototype. This allows you to create variations of a function to work with different data types, without having to think up a unique name for each variant.

In C++, operators are implemented as functions. By using function overloading on the operator functions, you can define your own versions of the operators that work with different data types (including classes that you’ve written). Using function overloading to overload operators is called operator overloading.

In this chapter, we’ll examine topics related to operator overloading.

Operators as functions

Consider the following example:

The compiler comes with a built-in version of the plus operator (+) for integer operands -- this function adds integers x and y together and returns an integer result. When you see the expression x + y, you can translate this in your head to the function call operator+(x, y) (where operator+ is the name of the function).

Now consider this similar snippet:

The compiler also comes with a built-in version of the plus operator (+) for double operands. Expression w + z becomes function call operator+(w, z), and function overloading is used to determine that the compiler should be calling the double version of this function instead of the integer version.

Now consider what happens if we try to add two object of a user-defined class:

What would you expect to happen in this case? The intuitive expected result is that the string “Hello, World!” would be printed on the screen. However, because Mystring is a user-defined class, the compiler does not have a built-in version of the plus operator that it can use for Mystring operands. So in this case, it will give us an error. In order to make it work like we want, we’d need to write an overloaded function to tell the compiler how the + operator should work with two operands of type Mystring. We’ll look at how to do this in the next lesson.

Resolving overloaded operators

When evaluating an expression containing an operator, the compiler uses the following rules:

  • If all of the operands are fundamental data types, the compiler will call a built-in routine if one exists. If one does not exist, the compiler will produce a compiler error.
  • If any of the operands are user data types (e.g. one of your classes, or an enum type), the compiler looks to see whether the type has a matching overloaded operator function that it can call. If it can’t find one, it will try to convert one or more of the user-defined type operands into fundamental data types so it can use a matching built-in operator (via an overloaded typecast, which we’ll cover later in this chapter). If that fails, then it will produce a compile error.

What are the limitations on operator overloading?

First, almost any existing operator in C++ can be overloaded. The exceptions are: conditional (?:), sizeof, scope (::), member selector (.), and member pointer selector (.*).

Second, you can only overload the operators that exist. You can not create new operators or rename existing operators. For example, you could not create an operator ** to do exponents.

Third, at least one of the operands in an overloaded operator must be a user-defined type. This means you can not overload the plus operator to work with one integer and one double. However, you could overload the plus operator to work with an integer and a Mystring.

Fourth, it is not possible to change the number of operands an operator supports.

Finally, all operators keep their default precedence and associativity (regardless of what they’re used for) and this can not be changed.

Some new programmers attempt to overload the bitwise XOR operator (^) to do exponentiation. However, in C++, operator^ has a lower precedence level than the basic arithmetic operators, which causes expressions to evaluate incorrectly.

In basic mathematics, exponentiation is resolved before basic arithmetic, so 4 + 3 ^ 2 resolves as 4 + (3 ^ 2) => 4 + 9 => 13.
However, in C++, the arithmetic operators have higher precedence than operator^, so 4 + 3 ^ 2 resolves as (4 + 3) ^ 2 => 7 ^ 2 => 49.

You’d need to explicitly parenthesize the exponent portion (e.g. 4 + (3 ^ 2)) every time you used it for this to work properly, which isn’t intuitive, and is potentially error-prone.

Because of this precedence issue, it’s generally a good idea to use operators only in an analogous way to their original intent.

Rule: When overloading operators, it’s best to keep the function of the operators as close to the original intent of the operators as possible.

Furthermore, because operators don’t have descriptive names, it’s not always clear what they are intended to do. For example, operator+ might be a reasonable choice for a string class to do concatenation of strings. But what about operator-? What would you expect that to do? It’s unclear.

Rule: If the meaning of an operator when applied to a custom class is not clear and intuitive, use a named function instead.

Within those confines, you will still find plenty of useful functionality to overload for your custom classes! You can overload the + operator to concatenate your user-defined string class, or add two Fraction class objects together. You can overload the << operator to make it easy to print your class to the screen (or a file). You can overload the equality operator (==) to compare two class objects. This makes operator overloading one of the most useful features in C++ -- simply because it allows you to work with your classes in a more intuitive way.

In the upcoming lessons, we’ll take a deeper look at overloading different kinds of operators.

9.2 -- Overloading the arithmetic operators using friend functions
8.x -- Chapter 8 comprehensive quiz

26 comments to 9.1 — Introduction to operator overloading

  • Jujinko

    Hey Alex,

    in the first two examples you wrote cout without the ‘std::’ as if you had called "using" before, which you didn’t.
    The example is still understandable, but for consistency you might wanna change that.
    Greetings, J

  • Mehul

    ALex this is the best site which i am following from 2014 to learn the concepts, Thanks a lot to make the concepts easily understandable  đŸ™‚

  • Jim

    Hello Alex,

    Could you please explain overloading of new and delete operator? I really appreciate any help that you can provide.

    • Alex

      Overloading new and delete is an advanced topic and probably worthy of its own separate tutorial. I can’t really do it justice in a response to a comment.

  • A

    Do the next lessons build up on operator overloading ?
    Little short on time 🙂

  • NotAMeme

    Whats the flight velocity of an unladen swallow?

  • mashariki

    2013 and loving your site! C++ well explained and very easy to understand..have tried out other sites but this is the best..much thanks from nairobi.

  • peter

    Hi Alex,

    Is there any reason why we cannot overload the following operators:

    arithmetic if (?:), sizeof, scope (::), member selector (.), and member pointer selector (.*)

    Does any one have ideas?

    • jadon

      because overloading those operators wouldnt make a whole lot of sense. and some real ambiguity would occur between normally happens and the calling the overload method.

    • Alex

      When Stroustrup was designing C++, he decided that operators that were close to the core of the language should not be overridable, because it would lead to ambiguity or confusion. His goal was to allow user-defined classes to work analogously to built-in types, not to allow you redefine how core language mechanisms work.

  • Renu

    I didnt understand this
    “all operators keep their current precedence and associativity, regardless of what they’re used for. For example, the bitwise XOR operator (^) could be overloaded to do exponents, except it has the wrong precedence and associativity and there is no way to change this ”

    Could you please elaborate this?

    • Consider the following mathematical equation: 5 * 2 ^ 3

      Using standard mathematical precedence rules, exponents are evaluated before multiplication. Thus, we’d solve this equation as: 5 * (2 ^ 3), which evaluates to 5 * 8 = 40.

      Now, let’s say we overloaded the XOR operator (^) to make it do exponents instead of bitwise XOR. According to C++ precedence rules, the multiplication operator takes precedence over the XOR operator.
      As a result, C++ would evaluate our mathematical equation above like this: (5 * 2) ^ 3. Consequently, it would produce the result 1000 instead of 40. This would be unintuitive to someone expecting the equation to be evaluated according to normal mathematical precedence rules. Thus, it would not be a good idea to overload the XOR operator in this way.

      Unfortunately, there is no way to change the precedence of operators in C++.

      • mdg32871

        In the example above you were talking about overloading the XOR operator (^). Using this example to help me understand operator overloading, how would you go about overloading the ^? How would it be written? I realize that in this instance you wouldn’t want to. I am just trying to understand this concept.

      • vkavicarm

        Hi Alex, how does the compiler differenciate as in if ^ is used for XOR or exponent?

  • Abhishek

    compilers are very smart!

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter