13.2 — Overloading the arithmetic operators using friend functions

Some of the most commonly used operators in C++ are the arithmetic operators -- that is, the plus operator (+), minus operator (-), multiplication operator (*), and division operator (/). Note that all of the arithmetic operators are binary operators -- meaning they take two operands -- one on each side of the operator. All four of these operators are overloaded in the exact same way.

It turns out that there are three different ways to overload operators: the member function way, the friend function way, and the normal function way. In this lesson, we’ll cover the friend function way (because it’s more intuitive for most binary operators). Next lesson, we’ll discuss the normal function way. Finally, in a later lesson in this chapter, we’ll cover the member function way. And, of course, we’ll also summarize when to use each in more detail.

Overloading operators using friend functions

Consider the following trivial class:

The following example shows how to overload operator plus (+) in order to add two “Cents” objects together:

This produces the result:

I have 14 cents.

Overloading the plus operator (+) is as simple as declaring a function named operator+, giving it two parameters of the type of the operands we want to add, picking an appropriate return type, and then writing the function.

In the case of our Cents object, implementing our operator+() function is very simple. First, the parameter types: in this version of operator+, we are going to add two Cents objects together, so our function will take two objects of type Cents. Second, the return type: our operator+ is going to return a result of type Cents, so that’s our return type.

Finally, implementation: to add two Cents objects together, we really need to add the m_cents member from each Cents object. Because our overloaded operator+() function is a friend of the class, we can access the m_cents member of our parameters directly. Also, because m_cents is an integer, and C++ knows how to add integers together using the built-in version of the plus operator that works with integer operands, we can simply use the + operator to do the adding.

Overloading the subtraction operator (-) is simple as well:

Overloading the multiplication operator (*) and the division operator (/) is as easy as defining functions for operator* and operator/ respectively.

Friend functions can be defined inside the class

Even though friend functions are not members of the class, they can still be defined inside the class if desired:

We generally don’t recommend this, as non-trivial function definitions are better kept in a separate .cpp file, outside of the class definition. However, we will use this pattern in future tutorials to keep the examples concise.

Overloading operators for operands of different types

Often it is the case that you want your overloaded operators to work with operands that are different types. For example, if we have Cents(4), we may want to add the integer 6 to this to produce the result Cents(10).

When C++ evaluates the expression x + y, x becomes the first parameter, and y becomes the second parameter. When x and y have the same type, it does not matter if you add x + y or y + x -- either way, the same version of operator+ gets called. However, when the operands have different types, x + y does not call the same function as y + x.

For example, Cents(4) + 6 would call operator+(Cents, int), and 6 + Cents(4) would call operator+(int, Cents). Consequently, whenever we overload binary operators for operands of different types, we actually need to write two functions -- one for each case. Here is an example of that:

Note that both overloaded functions have the same implementation -- that’s because they do the same thing, they just take their parameters in a different order.

Another example

Let’s take a look at another example:

The MinMax class keeps track of the minimum and maximum values that it has seen so far. We have overloaded the + operator 3 times, so that we can add two MinMax objects together, or add integers to MinMax objects.

This example produces the result:

Result: (3, 16)

which you will note is the minimum and maximum values that we added to mFinal.

Let’s talk a little bit more about how “MinMax mFinal = m1 + m2 + 5 + 8 + m3 + 16” evaluates. Remember that operator+ has higher precedence than operator=, and operator+ evaluates from left to right, so m1 + m2 evaluate first. This becomes a call to operator+(m1, m2), which produces the return value MinMax(8, 15). Then MinMax(8, 15) + 5 evaluates next. This becomes a call to operator+(MinMax(8, 15), 5), which produces return value MinMax(5, 15). Then MinMax(5, 15) + 8 evaluates in the same way to produce MinMax(5, 15). Then MinMax(5, 15) + m3 evaluates to produce MinMax(3, 15). And finally, MinMax(3, 15) + 16 evaluates to MinMax(3, 16). This final result is then assigned to mFinal.

In other words, this expression evaluates as “MinMax mFinal = (((((m1 + m2) + 5) + 8) + m3) + 16)”, with each successive operation returning a MinMax object that becomes the left-hand operand for the following operator.

Implementing operators using other operators

In the above example, note that we defined operator+(int, MinMax) by calling operator+(MinMax, int) (which produces the same result). This allows us to reduce the implementation of operator+(int, MinMax) to a single line, making our code easier to maintain by minimizing redundancy and making the function simpler to understand.

It is often possible to define overloaded operators by calling other overloaded operators. You should do so if and when doing so produces simpler code. In cases where the implementation is trivial (e.g. a single line) it’s often not worth doing this, as the added indirection of an additional function call is more complicated than just implementing the function directly.

Quiz time

Question #1

a) Write a class named Fraction that has a integer numerator and denominator member. Write a print() function that prints out the fraction.

The following code should compile:

This should print:


Show Solution

b) Add overloaded multiplication operators to handle multiplication between a Fraction and integer, and between two Fractions. Use the friend function method.

Hint: To multiply two fractions, first multiply the two numerators together, and then multiply the two denominators together. To multiply a fraction and an integer, multiply the numerator of the fraction by the integer and leave the denominator alone.

The following code should compile:

This should print:


Show Solution

c) Why does the program continue to work correctly if we remove the operators for integer multiplication from the previous solution?

Show Solution

d) If we remove the const from the Fraction * Fraction operator, the following line from the main function no longer works. Why?

Show Solution

e) Extra credit: the fraction 2/4 is the same as 1/2, but 2/4 is not reduced to the lowest terms. We can reduce any given fraction to lowest terms by finding the greatest common divisor (GCD) between the numerator and denominator, and then dividing both the numerator and denominator by the GCD.

std::gcd was added to the standard library in C++17 (in the <numeric> header).

If you’re on an older compiler, you can use this function to find the GCD:

Write a member function named reduce() that reduces your fraction. Make sure all fractions are properly reduced.

The following should compile:

And produce the result:


Show Solution

13.3 -- Overloading operators using normal functions
13.1 -- Introduction to operator overloading

366 comments to 13.2 — Overloading the arithmetic operators using friend functions

  • Navneet

    "Friend functions can be defined inside the class"
    could you please help me with this code :

    how do i call base4Friend() ??

  • Waldo Lemmer

    - Member functions should be zero-initialized
    - This lesson should use constructor member initializer lists
    - `type& identifier` at Q#1
    - Q#3e:

      `(a > 0 ? a : -a)` is ugly, it can be `std::abs(a)`


    I wrote an iterative version of gcd():

    • Waldo Lemmer

      I just discovered that the standard library has std::gcd (, maybe mention it at Q#3e?

  • Foo

    Hello, what is the difference between the following lines? All seem to be returning the same value:

  • Rishi

    >c) Why does the program continue to work correctly if we remove the operators for integer multiplication from the previous solution?

    Doubt: No, it doesn't work for me. See my code and error message below.

    *<stdin>:52:21: error: invalid operands to binary expression ('Fraction' and 'int')
        Fraction f4{ f1 * 2 };
                     ~~ ^ ~
    <stdin>:24:10: note: candidate function not viable: no known conversion from 'int' to 'const Fraction' for 2nd argument
    Fraction operator*(const Fraction& f1,const Fraction& f2)
    <stdin>:55:20: error: invalid operands to binary expression ('int' and 'Fraction')
        Fraction f5{ 2 * f2 };
                     ~ ^ ~~
    <stdin>:24:10: note: candidate function not viable: no known conversion from 'int' to 'const Fraction' for 1st argument
    Fraction operator*(const Fraction& f1,const Fraction& f2)
    2 errors generated.

  • Cemre

    I'm curious about why did you call 'gcd' method via (::) operator when reduce() is a non-static method?

    • nascardriver

      We can't call

      Because `gcd`, despite not having been fully created yet, is a variable and we can't call a variable. We need to tell the compiler that we want to call the static `gcd()` member function. We do that by using the qualified name.

  • Thaneja

    I find it difficult to understand how this works
    friend Fraction operator* (const Fraction& f1, const Fraction& f2)
        return { f1.m_n * f2.m_n, f1.m_d * f2.m_d };

    From the above snippet I am unable to figure out how this function returns a "Fraction" datatype.

    But, I can understand the below snippet

    friend Fraction operator*(const Fraction &f1, const Fraction &f2)
            Fraction f_temp(f1.m_numerator*f2.m_numerator, f1.m_denominator*f2.m_denominator);
            return f_temp;

    • Alex

      The compiler knows that the return value for operator* is of type Fraction, so in the former case, we don't need to explicitly tell the compiler that the object we're returning is a Fraction object. The compiler can infer it.

      It's equivalent to this:

  • kio

    Hi Alex and Nascardriver,

    I can't figure out why I'm getting the following output, and where I'm wrong.


    • kio

      Can you please demistify this

      And solution for (c:)
      We’re multiplying temporary Fraction objects, but non-const references cannot bind to temporaries.

      I'm bit confused again about r-values and l-values.

      • Alex

        l-values are things with addresses, like variables. r-values are things like literals and temporary objects (e.g. Fraction(1,2)) that don't have a persistent address.

        Non-const references can only bind to modifiable l-values. So if operator* took a non-const reference, we wouldn't be able to pass in a const l-value or an r-value to the operator, which would limit our ability to use it effectively.

    • nascardriver

      Your compiler warnings are set too low or you're ignoring them.

      Regarding (c), the `f1` and `f2` parameters are `const` references, they can bind to temporary variables. Without the `const`, you wouldn't be able to do `Fraction{1,1} * Fraction{1,1}`.

      • kio

        The compiler lvl is 4. I don't get any of the warnings driver. I'm trying to debug it, it seems that the problem might be in the constructor, but I'm not sure why is it the problem.

        • nascardriver

          Assuming you're using visual studio, with /Wall you should get warning C4547

 (Top right, ignore all C:/ warnings)

          (Note also clang's error. Default arguments should be at the declaration, not the definition)

    • Haldhar Patel

      just use curly braces when returning from overloaded functions, and you will get right outputs.
      As nascardriver told before that  list initialization knows the return type , even if we don't mention it.

      is similar to

      but I think

      is not similar to

  • Question 1 a)

    • Question 1 b)

    • Question 1 e)

  • Rishi

    How come the code I written for the second quiz, which is incomplete, works just fine and produces the desired output?

  • Rishi

    In the above program, when we declare the friend function, does the class name we use there refer to the constructor or return type? I don't quite get this concept especially after seeing this example. So excuse me if my question is meaningless

    • nascardriver

      That's the return type. Those operators are no class-members, they're free function. If they were members of a class named `Math`, it'd be

  • yeokaiwei

    1. Feedback on Overloading
    The 9.2 Quiz is a really good tutorial.

    a. The progression is step by step a, b, c, d, f1, f2, f3, f4, f5, etc
    b. There is no modification ie erase old lousy code and replace it.
    c. There are test values with expected output

    2. Feedback on Part C)
    I had a different scenario.

    Removing the 2 functions gives 2 errors in Microsoft Visual Studio 2019. LNK 2019 and LNK 1120.

    3. Quiz 1
    Thanks for the practice.

    I hope this is acceptable.

    • Hello Yeokaiwei

      This is unacceptable.

      You have not properly reduced all fractions. When the numerator is 0 you should return 0, not 0/6.

      Likewise, when denominator|numerator you should return a whole number: e.g. 6/2 should return 3.

      Again, this work is unacceptable

      • yeokaiwei

        Thanks, LiuLiuLiu.

        I didn't notice that.

        However, 0/6 is the expected result for the last answer.

        Is the quiz wrong?

        I'll redo this.

      • yeokaiwei

        Thanks for catching the error.

        It prints out the gcd and the correct answer now.

  • Write a Calculator class that with the help of overloaded functions can perform arithmetic operations on int and double values.
    Find the answer

  • pigiou

    Hello. Why are we able to write the following:

    cents1 + cents2 returns a Cents instance, but the Cents definition does not contain a constructor of type Cents(Cents).
    Am I missing something here? Why are we able to initialize Cents with a Cents object, since no such constructor is written?

  • Tony Kolarek

    Hey nascar, I have some troubles understanding this code:

    I tried using a debugger but I don't get how this works.
    Could you explain how the program works after centsSum{cents1 + cents2}?

    • nascardriver

      `getCents()` returns an `int` which is then printed to `std::cout`.
      Lesson 8.10 and 2.2

      • Tony Kolarek

        Sorry, I meant the centsSum{cents1 + cents2} part. I didn't completely understand why we could call operator+, why it had to return Cents() etc. Now I got it, especially thanks to the exercises!

        Thanks for the great tutorials and help :-D

  • srt1104

    gcd of 0 and 6 is 6. Therefore the fraction should reduce to 0/1 but in the output it's given 0/6 for the f7 Fraction.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]