C++ static cast with r-value references -- why do they work? - c++

I'm looking at a code snippet which compiles fine;
int a = 10;
int&& b = static_cast<int&&>(a);
The way I'm seeing it, a thing of type T&& is a reference to a temporary -- the underlying object isn't placed in memory.
But a exists in memory. Ergo, how can you have a r-value reference to it?

You're mixing up rvalues and temporaries. Temporaries are objects, rvalues are expressions.
The system of dividing expressions up into the categories lvalue, xvalue, prvalue is designed to help avoid logic errors in the program (for example, preventing 3 = 4; ) but it doesn't have any deeper meaning.
Any object (temporary or not) can be designated by an lvalue expression or an rvalue expression if we really want to . In the case in the question we make an xvalue expression that designates a.
Then we initialize a reference, which binds directly since the type is the same (note: the type , int, not the category).
The code has exactly the same effect as int& b = a; with one single exception, the result of decltype(b).
The comment makes a good point as well -- temporary objects still exist in memory (in the abstract machine) , they are just a different storage class to other objects. And in the case of lifetime-extended temporaries they behave very much like objects of automatic storage.
Going the other way, you can have an lvalue expression designating a temporary object, int const& x = 5; results in the lvalue expression x designating a temporary that has had its lifetime extended.
Or even without lifetime extension, std::string{} = "x" is an lvalue since operator= returns lvalue reference. You could write auto z = (std::string{} = "x") = "y"; which is not a good code style of course but understanding it helps to understand the type and value category system .

Related

Is there an implicit cast from temporary values to objects of type rvalue reference?

Consider this toy example:
struct A { int x; };
int main() {
A&& a=A();
return 0;
}
Is there an implicit cast from temporary values (values of built-in type, and temporary objects) to objects of type rvalue reference?
Does a has an address?
Is there an implicit cast from temporary values (values of built-in type, and temporary objects) to objects of type rvalue reference?
First a nitpick, casts are explicit conversions by definition, so there cannot be an implicit cast. Beyond that, a reference is distinctly not an object type. It may even not require any storage. References are bound to objects. It'd be more appropriate to think about them as alternative names of objects. Rvalue references in particular can only bind to objects of a particular sort. Which are roughly all "nameless" and "temporary" values. We may also cast to an rvalue reference to force an object being designated as "about to expire".
There's however an intricate point here, once an rvalue reference binds to an object, it's no longer "nameless". In particular, this means that by using that reference we call an object "by name" and thus obtain an lvalue. Furthermore, and to coincide with that, binding a reference in block scope to a temporary, prolongs the lifetime of the temporary until the end of the scope. And that brings us to the next point.
Does a has an address?
Yes and no. The refernece itself may or may not require storage, as I said previously. But that's moot, since it's just the name of an object. Taking the address of a reference will yield the address of the object that was bound to it. In this case, it will be the address of the temporary bound to it.

Rvalue to lvalue conversion?

Why it is not possible to convert rvalues to lvalues? It is possible to do a conversion in the opposite direction though. Technically rvalues do have a memory address, isn't it?
You can:
int&& x = 3;
x is now an lvalue. A so called 'rvalue-reference' can bind to a temporary,
but anything with a name is an lvalue, so you need to forward<>() it if you need it's rvalueness back.
Note that by binding a temporary to a rvalue-reference (or a const reference) you extend its lifetime. Technically a cast is possible,
but not recommended, since temporaries have short lifetime, so you
typically get a dangling reference.
It is rather straightforward to write a template function unmove(), that does the opposite of std::move():
template<class T> T& unmove(T&& t) { return t; }
Please note that, according to the standard since C++11:
a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference.
So it is safe to use unmove() within a single full expression, but after the expression has been fully evaluated, the temporaries go away.
My common use for unmove() is to call functions / methods, that return values through references, when I don't need those values.
Correct an anwer above.
int&& x = 3;
x is 'rvalue-reference'. See
https://www.tutorialspoint.com/What-is-double-address-operator-and-and-in-Cplusplus

Why is returned object an rvalue?

class A {}
A foo() {
A a;
// some work
return a;
}
Here it returns an instance of A, and I saw many readings saying that this returns a rvalue.
My confusion is, since it's perfectly legit to do A b; a = b;, the variable a seems to be an lvalue. So why does it become rvalue when it's the returned one?
There is no such thing as an rvalue object.
Rvalue and lvalue refers to expressions.
One expression that refers to an object can be an rvalue, while another that refers to the same object can be an lvalue.
Originally "lvalue" referred to any expression that could be on the left hand side of C's =, while "rvalue" was any expression that could only be on the right side. Since then C++ has acquired const and references, and things have got complicated. But the early C view of things is useful to understand this.
Regarding …
” the variable a seems to be an lvalue. So why does it become rvalue when it's the returned one?
Well, the variable in itself is not an lvalue. The expression a is an lvalue. And the function does not return the variable: it returns the variable's value.
So, as I understand it there are two misconceptions involved in the question:
The idea that lvalue/rvalue is about objects. It's about expressions.
The idea that A foo() { return... } return a reference to an object (as in e.g. Java). It returns a value.
An lvalue is an expression that refers to a memory location and allows us to take the address of that memory location via the & operator. An rvalue is an expression that is not an lvalue.
See here http://thbecker.net/articles/rvalue_references/section_01.html
In your example, you're returning a copy of a stack allocated instance. But that is likely also on the stack and is a 'temporary copy', hence you cannot take the address of it. Hence it's not an lvalue, therefore it's an rvalue

Bind an lvalue reference to an rvalue reference?

I have tried to compile:
int &&a=3;
int &b=a;
And it work.
I know that "a" is an lvalue, but why i can bind an "rvalue reference to int" to an "lvalue reference to int (not to an rvalue reference to int)"?
This way, i can change the value of the temporary, 3:
b=5 //changing the value of the temporary bound to the rvalue reference
This tecnique is used from std::forward, so i suppose it is a standard behavior.
Is an rvalue reference to int considered as a simple int lvalue storing a temporary?
If not, how do you explain the binding?
References don't bind to other references; they bind to objects.
When you initialise a from 3, you create a new, temporary int object whose lifetime is extended. b is simply another reference to that object.
Note that a is an lvalue, because it is an expression naming an object, even though it has type "rvalue-reference to int"! Be careful not to confuse types with value categories, here: types relate to objects, but value categories relate to expressions (even if those expressions name or otherwise evaluate to some object).
These confusing rules all fit in together quite neatly when you think about it: for the original reference to have been valid, the object's lifetime must have been extended, so b's initialisation is safe.

Are all temporaries rvalues in C++?

I have been coding in C++ for past few years. But there is one question that I have not been able to figure out. I want to ask, are all temporaries in C++, rvalues?
If no, can anyone provide me an example where temporary produced in the code is an lvalue?
No.
The C++ language specification never makes such a straightforward assertion as the one you are asking about. It doesn't say anywhere in the language standard that "all temporary objects are rvalues". Moreover, the question itself is a bit of misnomer, since the property of being an rvalue in the C++ language is not a property of an object, but rather a property of an expression (i.e. a property of its result). This is actually how it is defined in the language specification: for different kinds of expressions it says when the result is an lvalue and when it is an rvalue. Among other things, this actually means that a temporary object can be accessed as an rvalue as well as an lvalue, depending on the specific form of expression that is used to perform the access.
For example, the result of literal 2 + 3 expression is obviously an rvalue, a temporary of type int. We cannot apply the unary & to it since unary & requires an lvalue as its operand
&(2 + 3); // ERROR, lvalue required
However, as we all know, a constant reference can be attached to a temporary object, as in
const int &ri = 2 + 3;
In this case the reference is attached to the temporary, extending the lifetime of the latter. Obviously, once it is done, we have access to that very same temporary as an lvalue ri, since references are always lvalues. For example, we can easily and legally apply the unary & to the reference and obtain a pointer to the temporary
const int *pi = &ri;
with that pointer remaining perfectly valid as long as the temporary persists.
Another obvious example of lvalue access to a temporary object is when we access a temporary object of class type through its this pointer. The result of *this is an lvalue (as is always the case with the result of unary * applied to a data pointer), yet it doesn't change the fact that the actual object might easily be a temporary. For a given class type T, expression T() is an rvalue, as explicitly stated in the language standard, yet the temporary object accessed through *T().get_this() expression (with the obvious implementation of T::get_this()) is an lvalue. Unlike the previous example, this method allows you to immediately obtain a non-const-qualified lvalue, which refers to a temporary object.
So, once again, the very same temporary object might easily be "seen" as an rvalue or as an lvalue depending on what kind of expression (what kind of access path) you use to "look" at that object.
Prasoon Saurav already linked a very good clc++ thread. In there, James Kanze explains why the question doesn't really make sense. It boils down to:
rvalue-ness is a (boolean) property of expressions - each expression is either an lvalue or an rvalue
temporaries are not expressions
For that reason, the question doesn't make sense.
A good example is the following code:
int main() {
const int& ri = 4;
std::cout << ri << std::endl;
}
The temporary int with value 4 is not an expression. The expression ri that's printed is not a temporary. It's an lvalue, and refers to a temporary.
well, that array operator returns a reference, any function that returns a reference could be considered to do the same thing? all references are const, while they can be lvalues, they modify what they reference, not the reference itself. same is true for the *operator,
*(a temp pointer) = val;
I swear I used to use some compiler that would pass temp values to any function that took a reference,
so you could go:
int Afunc()
{
return 5;
}
int anotherFunc(int & b)
{
b = 34;
}
anotherFunc(Afunc());
can't find one that lets you do that now though, the reference has to be const in order to allow passing of temp values.
int anotherFunc(const int & b);
anyway, references can be lvalues and temporary, the trick being the reference it's self is not modified, only what it references.
if you count the-> operator as an operator, then temporary pointers can be lvalues, but the same condition applies, its not the temp pointer that would be changed, but the thing that it points to.
An array indexing operation is both a temporary and an lvalue, something like a[10] = 1 is an example of what you're looking for; the lvalue is a temporary, calculated pointer.
Short answer: yes, but I'm not going to quote the standard, because proving the point would require addressing every kind of temporary there is. By definition a temporary has a lifetime of one statement, so assigning things to one would be poor style at best.
Interesting answer: Copy elision can make (often makes) a temporary object identical with an lvalue object. For example,
MyClass blah = MyClass( 3 ); // temporary likely to be optimized out
or
return MyClass( 3 ); // likely to directly initialize object in caller's frame
Edit: as for the question of whether there is any temporary object in those cases, §12.8/15 mentions
the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy
which would indicate that there is a temporary object which may be identical with an lvalue.
It depends on what you consider a temporary variable is. You can write something like
#include <stdio.h>
int main()
{
char carray[10];
char *c=carray+1;
*(c+2+4) = 9;
printf("%d\n",carray[7]);
return 0;
}
This runs in VisualStudios and GCC. You can run the code in codepad
I consider (c+2+4) a rvalue although i want to assign to it. When i dereference it, it would become an lvalue. So yes all temporaries are rvalues. But you can make rvalues (thus a temporary) into an lvalue by dereferencing it
If no, can anyone provide me an example where temporary produced in the code is an lvalue?
The following code binds a constant reference to a temporary object of type const float created by the compiler:
int i;
const float &cfr = i;
The behaviour is "as if":
int i;
const float __tmp_cfr = i; // introduced by the compiler
const float &cfr = __tmp_cfr;