13.6 — Overloading unary operators +, -, and !

Overloading unary operators

Unlike the operators you’ve seen so far, the positive (+), negative (-) and logical not (!) operators all are unary operators, which means they only operate on one operand. Because they only operate on the object they are applied to, typically unary operator overloads are implemented as member functions. All three operands are implemented in an identical manner.

Let’s take a look at how we’d implement operator- on the Cents class we used in a previous example:

This should be straightforward. Our overloaded negative operator (-) is a unary operator implemented as a member function, so it takes no parameters (it operates on the *this object). It returns a Cents object that is the negation of the original Cents value. Because operator- does not modify the Cents object, we can (and should) make it a const function (so it can be called on const Cents objects).

Note that there’s no confusion between the negative operator- and the minus operator- since they have a different number of parameters.

Here’s another example. The ! operator is the logical negation operator -- if an expression evaluates to “true”, operator! will return false, and vice-versa. We commonly see this applied to boolean variables to test whether they are true or not:

For integers, 0 evaluates to false, and anything else to true, so operator! as applied to integers will return true for an integer value of 0 and false otherwise.

Extending the concept, we can say that operator! should evaluate to true if the state of the object is “false”, “zero”, or whatever the default initialization state is.

The following example shows an overload of both operator- and operator! for a user-defined Point class:

The overloaded operator! for this class returns the Boolean value “true” if the Point is set to the default value at coordinate (0.0, 0.0, 0.0). Thus, the above code produces the result:

point is set at the origin.

Quiz time

1) Implement overloaded operator+ for the Point class.

Show Solution

13.7 -- Overloading the comparison operators
13.5 -- Overloading operators using member functions

120 comments to 13.6 — Overloading unary operators +, -, and !

  • Waldo Lemmer

    - First code block, line 9 should use a member initializer list
    - Last code block (before quiz), line 34:


      Is comparison with 0.0 considered safe in this case?

    • Alex

      Yes, 0.0 can be represented exactly, so direct comparison with 0.0 is safe so long as you've directly initialized/assigned the 0.0 values. It might not be safe if you've done a bunch of math on the variables first and incurred some rounding error.

  • Imagine

    Why was () used to initialize nickle instead of {} here?

    • Imagine

      Also, why is this  

      and this

      same, which is better?

      • Alex

        They are the same. In the top one, we're being explicit about the fact that we're returning a Cents object. In the bottom one, it's implicit.

        I've updated to use the bottom one since the extra verbosity doesn't really add anything.

        • Imagine

          Thanks, that was a small thing but it was really eating me.

        • JamesC

          It looks quite confusing, like you're returning an int, though. In my opinion, you should at least point this out. Or write it as return { -m_cents }; to at least make it look like you're returning an anonymous object as opposed to just an int.

    • Alex

      The lesson was written before brace-initialization was a thing. I've updated it to use braces.

  • Mahmoud

    when i implement the operator- by value , to get the value i must write :

    but when i implemented it by refenece i can write the

    so why in the first case(returning by value) i can't write -obj directelly like this

    Note: i implemented operator<< .

  • J34NP3T3R

    i got confused a little bit with the unary + and - operators.

    for some reason i got it in my head that the unary - turns an integer into negative and a + turns an integer into positive
    but after reviewing 5.2 — Arithmetic operators it seems the unary + does not turn an integer ( e.g. -5 ) into a positive integer. was just added for symmetry.
    std::abs does that.

    • cjames

      Unary + does not change the sign of its operand (just like in mathematics). It applies integral promotion, and can also be used to check whether its operand is an arithmetic type or not (sometimes useful in macros). You can read more about it here:

  • Question 1

  • yeokaiwei

    1. Feedback
    Could we just use 1 super example per chapter?

    It is confusing to switch between Cents, Points, Fractions.

    That way, students will remember your lessons properly and you can constantly re-use and upgrade the example in 1 single chapter.

    When students learn <insert-name-for-super-example-for-Chapter-9>, they will associate that super example with overloading.

    E.g. Chapter 9
    The Points super example.

  • Nick

    "It returns a Cents object that is the negation of the original Cents value. Because operator- does not modify the Cents object, we can (and should) make it a const function"

    I don't understand. You return the negation of the original Cents value, therefore you're modifying the Cents objects; but you explicitly said this doesn't modify it. Could you explain please?

  • Hmmmm. In your operator! in the Point example you're comparing floating-point numbers to 0.0. I thought that was not a good idea because a) FP numbers shouldn't be checked for equality anyhow and b) there's no way to represent 0.0 precisely. Is this not so?

  • SaMIRa

    Is there any way to implement the following?

  • sv

    i did this and i thought output would be ---   "A nickle of debt is worth    hello   -5 cents"
    but the actual output is ---- "    hello    A nickle of debt is worth -5 cents"

    can someone explain why did this happen?
    whats the flow of control here?

    • nascardriver

      It's undefined, the functions can be called in any order. If you need functions to be called in a specific order, call them one after the other.

      • Oleg+Revedzhuk

        So the way I understand this is that the std::cout part is an expressin, so it looks like this:

        And there is no confusion which order it gets resolved in, its inner () first, but then when you get to (-nickle).getCents() it becomes getCents(operator-(nickle)).

        So to me that looks ok as well because you have the inner function call and then the outer function call. I thought that undefined behavior happens if you expected function parameters to be evaluated in a special order or something along those lines. Am I missing something from this example? Where exactly is the undefined behavior occurring?

        • nascardriver

          Because there are no overlapping modifications to a variable, it's not undefined behavior in this case.

          Your parentheses are correct, but there's still a function call within that expression.

          This expression will be evaluated from left to right, but the (4 + 5) can be evaluated at any point, even before the 1 is looked at. Evaluating (4 + 5) at the very beginning, or only when it's needed doesn't make a difference, because the (4 + 5) is independent of the rest, the result doesn't change.

          For the same reason, `operator-` and `getCents()` can be called at any point. The order of evaluation is different from operator precedence and associativity.

          • Oleg Revedzhuk

            Ok this makes perfect sense now! The issue wasn’t that he was making a function call there, but that the function call he was making had a “side effect”? in that it also did output. There isn’t a guarantee that the call is made in the order we want it to be made, and since it also outputs there’s the issue.

            If it was just modifying a value, then we wouldn’t have noticed.

            Thanks for the reply!

  • Nguyen

    "Extending the concept, we can say that operator! should evaluate to true if the state of the object is “false”, “zero”, or whatever the default initialization state is."

    What is the state of the object in the last example?  I can't relate the state of the object to the example.
    What I see is the condition (!point) in the if statement becomes true or false depending on the boolean value of true or false returned from the overloaded operator!

    • nascardriver

      "state" is the same as "values" in this case. The default initialization state is when all members have their default value (0).

  • David

    Hello,I have a question in your example. Before we call the operator- , the value of nickle is 5. However,when we call the operator-(),the value of nickle becomes -5. And why can you say the operator-() doesn't modify the object?
    In my opinion , its behavior violated the const meaning.
    Does my concept be wrong?

    • nascardriver

      The value of `nickle` doesn't change. `operator-` creates a new `Cents` object with value -5. You can print `nickle.getCents()` in line 27 to verify this.

  • Ged

    Is there any difference if this function is outside of the class or inside?

  • hellmet

    I was wondering why not have the operator- () function be left in the class itself?

  • Parsa

    what about returning just by reference? Not const reference.
    Is the object returned an r-value? So you can't return it by reference?

Leave a Comment

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