lvalue rvalue

Discussion in 'C++' started by Denis Remezov, Jul 16, 2004.

  1. JKop wrote:
    >
    > The following compiles:
    >
    > // syrup.cpp
    >
    > struct DoubleInDisguise
    > {
    > double data;
    > };
    >
    > double Chocolate1()
    > {
    > double blah = 67.22;
    >
    > return blah;
    > }
    >
    > DoubleInDisguise Chocolate2()
    > {
    > DoubleInDisguise blah = { 67.22 };
    >
    > return blah;
    > }
    >
    > /*
    > inline void Manipulate(double& input)
    > {
    > input = 222.76;
    > }
    > */
    >
    > inline void Manipulate(DoubleInDisguise& input)
    > {
    > //input.data = 222.76;
    > }
    >
    > int main()
    > {
    > //Manipulate( Chocolate1() );
    >
    > Chocolate2() = DoubleInDisguise();
    >
    > // Manipulate( Chocolate2() );
    > }
    >
    > See how the return-value from Chocolate2() can have an assigment done to it.
    > This suggests that its non-const first of all, and secondly that it's an
    > lvalue.


    [...]

    No, it is an rvalue. Yes, for built-in types, you can only assign to an
    lvalue. But since DoubleInDisguise is a user-defined type, it has a member
    function operator = (semantically, anyway), which can be called on an rvalue.

    To prevent this kind of confusion, you can change Chocolate2 to
    DoubleInDisguise const Chocolate2() {...}

    Denis
    Denis Remezov, Jul 16, 2004
    #1
    1. Advertising

  2. Denis Remezov

    JKop Guest

    The following compiles:


    // syrup.cpp

    struct DoubleInDisguise
    {
    double data;
    };

    double Chocolate1()
    {
    double blah = 67.22;

    return blah;
    }

    DoubleInDisguise Chocolate2()
    {
    DoubleInDisguise blah = { 67.22 };

    return blah;
    }


    /*
    inline void Manipulate(double& input)
    {
    input = 222.76;
    }
    */

    inline void Manipulate(DoubleInDisguise& input)
    {
    //input.data = 222.76;
    }

    int main()
    {
    //Manipulate( Chocolate1() );

    Chocolate2() = DoubleInDisguise();

    // Manipulate( Chocolate2() );
    }


    See how the return-value from Chocolate2() can have an assigment done to it.
    This suggests that its non-const first of all, and secondly that it's an
    lvalue. But now, see my last line of code, take away the // commenters. It
    won't compile. You can assign to a temporary, yet it can't act as a
    double&?! What the hell is going on?

    -JKop
    JKop, Jul 16, 2004
    #2
    1. Advertising

  3. JKop wrote:
    > The following compiles:
    >
    >
    > // syrup.cpp
    >
    > struct DoubleInDisguise
    > {
    > double data;
    > };
    >
    > double Chocolate1()
    > {
    > double blah = 67.22;
    >
    > return blah;
    > }
    >
    > DoubleInDisguise Chocolate2()
    > {
    > DoubleInDisguise blah = { 67.22 };
    >
    > return blah;
    > }
    >
    >
    > /*
    > inline void Manipulate(double& input)
    > {
    > input = 222.76;
    > }
    > */
    >
    > inline void Manipulate(DoubleInDisguise& input)
    > {
    > //input.data = 222.76;
    > }
    >
    > int main()
    > {
    > //Manipulate( Chocolate1() );
    >
    > Chocolate2() = DoubleInDisguise();



    The above line does this: Chocolate2() returns a temporary
    DoubleInDisguise object which is then assigned the value of the
    temporary on the right (which is initialised to 0).


    >
    > // Manipulate( Chocolate2() );




    The above takes a double as an argument while you are passing it a
    DoubleInDisguise.






    Regards,

    Ioannis Vranos

    http://www23.brinkster.com/noicys
    Ioannis Vranos, Jul 16, 2004
    #3
  4. Ioannis Vranos wrote:

    > JKop wrote:
    >
    >> The following compiles:
    >>
    >>
    >> // syrup.cpp
    >>
    >> struct DoubleInDisguise
    >> {
    >> double data;
    >> };
    >>
    >> double Chocolate1()
    >> {
    >> double blah = 67.22;
    >>
    >> return blah;
    >> }
    >>
    >> DoubleInDisguise Chocolate2()
    >> {
    >> DoubleInDisguise blah = { 67.22 };
    >>
    >> return blah;
    >> }
    >>
    >>
    >> /*
    >> inline void Manipulate(double& input)
    >> {
    >> input = 222.76;
    >> }
    >> */
    >>
    >> inline void Manipulate(DoubleInDisguise& input)
    >> {
    >> //input.data = 222.76;
    >> }
    >>
    >> int main()
    >> {
    >> //Manipulate( Chocolate1() );
    >>
    >> Chocolate2() = DoubleInDisguise();

    >
    >
    >
    > The above line does this: Chocolate2() returns a temporary
    > DoubleInDisguise object which is then assigned the value of the
    > temporary on the right (which is initialised to 0).
    >
    >
    >>
    >> // Manipulate( Chocolate2() );




    The above is passing to the void Manipulate(DoubleInDisguise&) a
    temporary of type DoubleInDisguise which is not allowed since it is
    getting a reference. The only way you can do it is by making the function

    inline void Manipulate(const DoubleInDisguise& input)
    {
    //input.data = 222.76;
    }


    that is with a const reference. I suggest you get and read TC++PL 3, you
    will find much knowledge in this book.






    Regards,

    Ioannis Vranos

    http://www23.brinkster.com/noicys
    Ioannis Vranos, Jul 16, 2004
    #4
  5. Denis Remezov

    DaKoadMunky Guest

    struct Foo
    {
    void DoSomething() {}
    };

    void DoSomethingWithFoo(Foo& foo)
    {
    foo.DoSomething();
    }

    int main()
    {
    //So in this context the temporary is non-const...
    Foo().DoSomething();

    //...but in this context the temporary is const?
    DoSomethingWithFoo(Foo());

    //Note that I can bind a reference to the original
    //temporary by doing the following. Is this
    //undefined behavior?
    DoSomethingWithFoo(Foo().operator=(Foo()));

    return 0;
    }

    Oh well.

    Maybe grokking this is beyond me. Time to move on.
    DaKoadMunky, Jul 16, 2004
    #5
  6. DaKoadMunky wrote:

    > struct Foo
    > {
    > void DoSomething() {}
    > };
    >
    > void DoSomethingWithFoo(Foo& foo)

    [7]> {
    > foo.DoSomething();
    > }
    >
    > int main()
    > {
    > //So in this context the temporary is non-const...
    > Foo().DoSomething();
    >
    > //...but in this context the temporary is const?

    [17]> DoSomethingWithFoo(Foo());
    >
    > //Note that I can bind a reference to the original
    > //temporary by doing the following. Is this
    > //undefined behavior?
    > DoSomethingWithFoo(Foo().operator=(Foo()));
    >
    > return 0;
    > }
    >
    > Oh well.
    >
    > Maybe grokking this is beyond me. Time to move on.




    > Executing: C:\Program Files\ConTEXT\ConExec.exe

    "c:\mingw\bin\g++.exe" -std=c++98 -pedantic-errors -Wall
    -fexpensive-optimizations -O3 -ffloat-store -mcpu=pentiumpro "temp.cpp"
    -o temp

    temp.cpp: In function `int main()':
    temp.cpp:17: error: could not convert `Foo()' to `Foo&'
    temp.cpp:7: error: in passing argument 1 of `void DoSomethingWithFoo(Foo&)'
    > Execution finished.







    Regards,

    Ioannis Vranos

    http://www23.brinkster.com/noicys
    Ioannis Vranos, Jul 16, 2004
    #6
  7. Denis Remezov

    DaKoadMunky Guest

    Using MSVC++.NET I also received an error, as expected, on line 17.

    I did not get an error on line 7.

    The online compiler at http://www.comeaucomputing.com/pcgi-bin/compiler.cgi set
    for Windows XP generated the same results...error on line 17 and no error on
    line 7.

    Of course using compilers is the wrong way to determine what is legal and
    illegal :)
    DaKoadMunky, Jul 17, 2004
    #7
  8. JKop wrote:
    > ...
    > int main()
    > {
    > //Manipulate( Chocolate1() );
    >
    > Chocolate2() = DoubleInDisguise();
    >
    > // Manipulate( Chocolate2() );
    > }
    >
    >
    > See how the return-value from Chocolate2() can have an assigment done to it.
    > This suggests that its non-const first of all, and secondly that it's an
    > lvalue.


    No, it doesn't suggest that it's an lvalue. Only built-in assignment
    operator requires an lvalue on it left-hand side. User-defined
    assignment operators don't have this requirement.

    > But now, see my last line of code, take away the // commenters. It
    > won't compile. You can assign to a temporary, yet it can't act as a
    > double&?! What the hell is going on?


    Non-constant references _cannot_ be bound to rvalues. Non-constant
    methods _can_ be called on non-constant rvalue objects. There's certain
    intuitive feeling of asymmetry here, but that's just the way it is in C++.

    --
    Best regards,
    Andrey Tarasevich
    Andrey Tarasevich, Jul 17, 2004
    #8
  9. DaKoadMunky wrote:
    > Using MSVC++.NET I also received an error, as expected, on line 17.
    >
    > I did not get an error on line 7.
    >
    > The online compiler at http://www.comeaucomputing.com/pcgi-bin/compiler.cgi set
    > for Windows XP generated the same results...error on line 17 and no error on
    > line 7.



    Yes the first error was indicated in line 17 and then it mentions line 7
    where there is the function declaration/definition itself.


    > Of course using compilers is the wrong way to determine what is legal and
    > illegal :)



    Yes I did not use the compiler in my original post, but when I saw your
    post with the "it works" attitude, I decided to use it in your code.


    Temporaries are destroyed just after you pass them to a function,
    including when the function is taking references, with the exception
    when it takes const references as arguments. In the last case, the
    temporary persists until it reaches the end of the function and this is
    the way many standard library algorithms work.






    Regards,

    Ioannis Vranos

    http://www23.brinkster.com/noicys
    Ioannis Vranos, Jul 17, 2004
    #9
  10. Denis Remezov

    Ali Cehreli Guest

    On Fri, 16 Jul 2004 16:23:17 -0700, Ioannis Vranos wrote:

    > Temporaries are destroyed just after you pass them to a function,


    They are destroyed at the end of the complete statement where they are
    created. The complete statement may have many other function
    calls. The destruction of the temporary must wait untill all of those
    calls are completed.

    > including when the function is taking references, with the exception
    > when it takes const references as arguments.


    We cannot pass temporaries to functions taking non-const references
    anyway. So I will limit the rest of the discussion to functions that
    take const references.

    > In the last case, the
    > temporary persists until it reaches the end of the function and this is
    > the way many standard library algorithms work.


    This is incorrect. The temporary lives longer than the function call
    as I described above. The destruction must wait until the statement
    completes.

    Here is a code that tries to demonstrates this:

    class C
    {
    int i_;

    public:
    C(int i) : i_(i) { cout << "construct\n"; }
    C(C const &) { cout << "copy\n"; }
    C & operator=(C const &) { cout << "assign\n"; }
    ~C() { cout << "destroy\n"; }

    void use() const {}
    };

    int foo(C const & c)
    {
    c.use();
    cout << "foo\n";
    return 0;
    }

    void bar(int, C const &)
    {
    cout << "bar\n";
    }

    C blah()
    {
    return 1;
    }

    int main()
    {
    bar(foo(blah()), C(0));
    }

    The two temporaries created must live until bar exits. The output of
    the program must have two 'destroy's printed *after* 'bar'.

    Ali
    Ali Cehreli, Jul 17, 2004
    #10
  11. Ali Cehreli wrote:

    > They are destroyed at the end of the complete statement where they are
    > created. The complete statement may have many other function
    > calls. The destruction of the temporary must wait untill all of those
    > calls are completed.
    >
    > This is incorrect. The temporary lives longer than the function call
    > as I described above. The destruction must wait until the statement
    > completes.



    Yes you are right.






    Regards,

    Ioannis Vranos

    http://www23.brinkster.com/noicys
    Ioannis Vranos, Jul 17, 2004
    #11
  12. Denis Remezov

    JKop Guest

    Andrey Tarasevich posted:

    > but that's just the way it is in C++.



    The only valid explanantion.


    -JKop
    JKop, Jul 17, 2004
    #12
  13. Denis Remezov

    JKop Guest

    I've just had a quick read over this thread.

    We can summarize temporaries with the following:

    non-const (they're *not* const)
    r-value (they're *not* an l-value)

    (strange how it is!)

    With has the following repercussions (for want of a better word):

    A temporary cannot be bound to a non-const reference ( not because it's
    const, because it *isn't*, but because it's an r-value, ie. you can only
    bind a non-const reference to an l-value ).

    So, where you have:

    AnyType& blah = Anything...

    then "Anything" must be:

    non-const
    l-value


    And where you have:

    AnyType const & blah = Anything...

    Then "Anything" can quite literally be anything!


    -JKop
    JKop, Oct 18, 2004
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Chris Mantoulidis

    ++x returns lvalue but x++ return rvalue

    Chris Mantoulidis, Dec 28, 2003, in forum: C++
    Replies:
    4
    Views:
    495
    Chris Mantoulidis
    Dec 29, 2003
  2. Gonzalo Aguirre

    rvalue / lvalue operator[]

    Gonzalo Aguirre, Jan 2, 2004, in forum: C++
    Replies:
    4
    Views:
    444
    Ron Natalie
    Jan 2, 2004
  3. Mark Stijnman
    Replies:
    2
    Views:
    477
    =?ISO-8859-15?Q?Juli=E1n?= Albo
    Apr 22, 2005
  4. Kavya
    Replies:
    9
    Views:
    509
    Dik T. Winter
    Oct 28, 2006
  5. Juha Nieminen
    Replies:
    13
    Views:
    613
    Edek Pienkowski
    Aug 29, 2012
Loading...

Share This Page