lvalue rvalue

D

Denis Remezov

JKop said:
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
 
J

JKop

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
 
I

Ioannis Vranos

JKop said:
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
 
I

Ioannis Vranos

Ioannis said:
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).



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
 
D

DaKoadMunky

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.
 
I

Ioannis Vranos

DaKoadMunky said:
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
 
D

DaKoadMunky

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 :)
 
A

Andrey Tarasevich

JKop said:
...
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++.
 
I

Ioannis Vranos

DaKoadMunky said:
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
 
A

Ali Cehreli

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
 
I

Ioannis Vranos

Ali said:
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
 
J

JKop

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top