Thread: Question about return value of * operator

  1. #1
    Registered User
    Join Date
    May 2013
    Posts
    228

    Question about return value of * operator

    Hi.
    I'm reading Effective C++ 3rd Edition, and I'm a bit confused about one of the given examples.
    Here's the relevant quote:
    Having a function return a constant value is generally inappropriate,
    but sometimes doing so can reduce the incidence of client errors with-
    out giving up safety or efficiency. For example, consider the declara-
    tion of the operator* function for rational numbers that is explored in
    Item 24:
    Code:
    class Rational { ... };
    const Rational operator*(const Rational& lhs, const Rational& rhs);
    Many programmers squint when they first see this. Why should the
    result of operator* be a const object? Because if it weren’t, clients
    would be able to commit atrocities like this:
    Code:
    Rational a, b, c;
    ...
    (a * b) = c; // invoke operator= on the result of a*b!
    I don’t know why any programmer would want to make an assignment
    to the product of two numbers, but I do know that many programmers
    have tried to do it without wanting to...
    The question is, why is the 'const' necessary in this particular example?
    Why isn't:
    Code:
    Rational operator*(const Rational& lhs, const Rational& rhs);
    enough?
    This function (without the const) returns an rvalue, which you cannot assign to anyway (compile-time error).
    What am I missing here?

  2. #2
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    This function (without the const) returns an rvalue, which you cannot assign to anyway (compile-time error). What am I missing here?
    O_o

    You are apparently missing the parts of standard C++ which allow you to modify an "rvalue" of a class type under many circumstances largely related to methods and method chaining.

    In other words, the code would not produce a compiler error without the `const'.

    You should, by the by, seek parity as much as possible with native types where those operations make sense.

    Soma

    Code:
    #include <iostream>
    
    struct S
    {
        void mutate()
        {
            ++m;
        }
        int m;
    };
    
    S Go()
    {
        return(S());
    }
    
    int main()
    {
        Go().mutate();
    }
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

  3. #3
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    The short answer is that, without the const, the return value can be used as an lvalue. With the const, it cannot.

    Yeah, okay .... that's essentially what Scott said.

    The longer answer is that "(a * b) = c" expands to operator*(a,b).operator=(c). Generally speaking, there is nothing preventing the call of a member function of an object returned by a function. However, operator=() generally is not a const member function (if it was, simple assignments like "a = b" would fail) so making operator*() return a const value prevents calling the operator=().
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  4. #4
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    It's not an r-value. You can basically fill out Rational and test this yourself, like I did, and see that (a * b) = c; is a legal construct, because of the lack of const correctness.

    Here is my gdb output. It's bad enough that we succeeded in a compile, but you can clearly see that the operator* is entered and then the return value, if you could access it anyway, is overwritten.
    Code:
    Reading symbols from /cygdrive/c/users/josh2/desktop/foo/rational...done.
    (gdb) break 6
    Breakpoint 1 at 0x4011f2: file main.cpp, line 6.
    (gdb) break 4
    Breakpoint 2 at 0x40119e: file main.cpp, line 4.
    (gdb) run
    Starting program: /cygdrive/c/users/josh2/desktop/foo/rational
    [New Thread 3492.0x538]
    [New Thread 3492.0xc44]
    
    Breakpoint 2, main () at main.cpp:5
    5           Rational a(2, 3), b(4, 5), c;
    (gdb) rwatch c
    Hardware read watchpoint 3: c
    (gdb) c
    Continuing.
    
    Breakpoint 1, main () at main.cpp:6
    6           (a * b) = c;
    (gdb) c
    Continuing.
    Hardware read watchpoint 3: c
    
    Value = {top = 1, bottom = 1}
    0x00401212 in main () at main.cpp:6
    6           (a * b) = c;
    (gdb)
    So yes, a const return value is necessary here if you want to avoid a very silly thing.

  5. #5
    Registered User
    Join Date
    May 2013
    Posts
    228
    For some reason I thought that every temporary ("Anonimous varaible") has an expression scope, and can't be the lhs of an assignment...

  6. #6
    Registered User
    Join Date
    May 2013
    Posts
    228
    Whiteflags; it seems like it is an rvalue, but, as phantomotap pointed out, it is legal to change an rvalue.

    Consider this class for example:
    Rational.h
    Code:
    class Rational {
    public:
        Rational();
        Rational(int nominator);
        Rational(int nominator, int denominator);
        Rational operator*(const Rational& rhs);
        virtual ~Rational();
    private:
        int m_nominator;
        int m_denominator;
    };

    Rational.c

    Code:
    #include "Rational.h"
    
    // more implamentation here...
    
    Rational Rational::operator *(const Rational& rhs) {
        return Rational((m_nominator*rhs.m_nominator),(m_denominator*rhs.m_denominator));
    }
    So now, the compiler will allow this:
    Code:
    int main() {
        Rational a,b,c;
        (a*b)=c;
    }
    But when I try to bind the return value to a reference (I know it's basically wrong, but I'm doing it for testing purposes) like this:

    Rational.h
    Code:
    class Rational {
    public:
        Rational();
        Rational(int nominator);
        Rational(int nominator, int denominator);
        Rational& operator*(const Rational& rhs);
        virtual ~Rational();
    private:
        int m_nominator;
        int m_denominator;
    };

    Rational.c

    Code:
    #include "Rational.h"
    
    // more implamentation here...
    
    Rational& Rational::operator *(const Rational& rhs) {
        return Rational((m_nominator*rhs.m_nominator),(m_denominator*rhs.m_denominator));
    }
    I get:
    error: invalid initialization of non-const reference of type ‘Rational&’ from an rvalue of type ‘Rational’



  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    The point isn't to prevent the assignment to what is supposed to be an r-value...

    The point is to prevent it from compiling when someone types "(a * b) = c" when they really meant "(a * b) == c". The way we do that is to prevent assignment to the r-value but that's just the tactic, not the strategy.

    Honestly I don't bother doing this because it only prevents this one particular thing (there are still plenty of ways open to make mistakes).
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. operator >> return value
    By Tool in forum C++ Programming
    Replies: 2
    Last Post: 06-27-2010, 02:52 AM
  2. reference operator following return type?
    By thracian in forum C++ Programming
    Replies: 22
    Last Post: 07-10-2008, 12:15 AM
  3. operator++() automatically return (*this)?
    By ray_broome in forum C++ Programming
    Replies: 3
    Last Post: 07-07-2004, 02:31 PM
  4. Weird Problem with return with operator +
    By KonArtis in forum C++ Programming
    Replies: 5
    Last Post: 05-05-2004, 03:46 PM
  5. Check the new operator's return value?
    By John.H in forum C++ Programming
    Replies: 2
    Last Post: 04-17-2003, 09:37 AM