I am puzzled by the following code:
#include <iostream>
int main()
{
int x{};
int&& rvx = static_cast<int&&>(x);
++rvx;
std::cout << x << std::endl;
}
The output of it is 1. I don't understand how this works. The static_cast is supposed to cast the lvalue x into an xvalue, which is then assigned to rvx. Why does incrementing rvx lead to a change in x? Is this because the converted lvalue-to-rvalue is essentially sitting at the same memory location, but it is just considered now a rvalue? I was under the impression (which is probably false) that somehow the cast creates a temporary out of its argument.
rvalue reference is a reference. In this, it works just like any other reference.
An rvalue reference can bind to a temporary. This is what you'd get, for instance, if you write
int x{};
int&& rvx = +x;
But it doesn't need to bind to a temporary. By casting x to int&&, you've indicated to the compiler that that's okay too, that it may treat x as an rvalue to bind directly to the reference.
Related
why this binding is ok
int main()
{
double d = 4.56;
const int &r = d;
return 0;
}
but this is not
int main()
{
double d = 4.56;
int &r = d;
return 0;
}
can anyone explain me while the first one compile but second one shows error
When you bind to a converted type (the double has to be converted to an int), you get a prvalue, since the converted int is a temporary with no address in memory. Therefore, binding a regular reference to it doesn't work, because they can only bind to glvalues. const type references can bind to prvalues, so the first one compiles still. Source: https://en.cppreference.com/w/cpp/language/value_category
An int reference cannot be bound to an object of type double, because the types mismatch.
So in order to make the initialization of the reference work at all, a new temporary object of the correct type int must be created. This is possible because there is an implicit conversion sequence from double to int.
The reference should then bind to this temporary, i.e. to a rvalue expression, but only const lvalue references are allowed to bind to rvalues. Non-const lvalue references are not allowed to do that, making the second program ill-formed.
Note that in the first program, although the reference is bound to a temporary object that would usually be destroyed at the end of the full-expression that it was created in, binding to a reference extends the lifetime of that temporary object to the lifetime of the reference. So using r in the first program is actually ok.
However, access through r will not refer to d, but to that new temporary object that is independent of d, which may be surprising, and therefore I think it is not a good idea to write like that. Use auto& or const auto& to make sure that r will certainly refer to d and that there is never any implicit conversion happening due to a type mismatch. If you want the conversion, just use int instead of a reference to int.
#include <iostream>
int&& func() {return 7;}
int main() {
std::cout << &func();
}
Produces an error, even with return std::move(7). It's saying func() is an rvalue (as if I'm doing &7).
It's saying func() is an rvalue
Well, it is an rvalue.
(as if I'm doing &7).
It is as if you're doing &7, at least in the sense that you can't use & on an rvalue.
In reality you're trying to use a dangling reference to a temporary int (initialised from a literal) as an lvalue.
This code makes no sense. I don't know what you're trying to do, but you can't do it.
#include <iostream>
using namespace std;
int main()
{
int i = 0;
cout << &i << endl;
const auto &ref = (short&&)i;
cout << &ref << endl;
return 0;
}
Why is &i different from &ref? (short&)i doesn't cause this problem. Does (short&&)i generate a temporary variable?
It's because you're doing a different type of cast. The C style explicit conversion cast does always a static cast, if it could be interpreted as a static cast; otherwise it does a reinterpret cast. And/or const cast as needed.
(short&&)i is a static cast because it can be interpreted as static_cast<short&&>(i). It creates a temporary short object, to which ref is bound. Being a different object, it has a different address.
(short&)i is a reinterpret cast because it cannot be interpreted as static_cast<short&>(i) which is ill formed. It reinterprets the int reference as short reference, and ref is bound to the the same object. Note that accessing the object through this reference would have undefined behaviour.
This creates a lvalue reference to a thing that exists:
const auto& ref = i;
The expressions &ref and &i will therefore give the same result.
This is also true of:
const auto& ref = (int&)i;
which is basically the same thing.
However, casting to something that is not a lvalue reference to T (so, to a value, or to an rvalue reference of another type!) must create a temporary; this temporary undergoes lifetime extension when bound to ref. But now ref does not "refer to" i, so the address-of results will differ.
It's actually a little more complicated than that, but you get the idea. Besides, don't write code like this! An int is not a short and you can't pretend that it is.
Apparently it creates a temporary.
Actually the compiler will tell you itself.
Try this:
auto &ref = (short&&)i;
cout << &ref << endl;
The error says:
error: non-const lvalue reference to type 'short' cannot bind to a
temporary of type 'short'
Test code here.
(short&&)i creates a temporary, so you take address of an other object, so address might differ.
Wrong form:
int &z = 12;
Correct form:
int y;
int &r = y;
Question:
Why is the first code wrong? What is the "meaning" of the error in the title?
C++03 3.10/1 says: "Every expression is either an lvalue or an rvalue." It's important to remember that lvalueness versus rvalueness is a property of expressions, not of objects.
Lvalues name objects that persist beyond a single expression. For example, obj , *ptr , ptr[index] , and ++x are all lvalues.
Rvalues are temporaries that evaporate at the end of the full-expression in which they live ("at the semicolon"). For example, 1729 , x + y , std::string("meow") , and x++ are all rvalues.
The address-of operator requires that its "operand shall be an lvalue". if we could take the address of one expression, the expression is an lvalue, otherwise it's an rvalue.
&obj; // valid
&12; //invalid
int &z = 12;
On the right hand side, a temporary object of type int is created from the integral literal 12, but the temporary cannot be bound to non-const reference. Hence the error. It is same as:
int &z = int(12); //still same error
Why a temporary gets created? Because a reference has to refer to an object in the memory, and for an object to exist, it has to be created first. Since the object is unnamed, it is a temporary object. It has no name. From this explanation, it became pretty much clear why the second case is fine.
A temporary object can be bound to const reference, which means, you can do this:
const int &z = 12; //ok
C++11 and Rvalue Reference:
For the sake of the completeness, I would like to add that C++11 has introduced rvalue-reference, which can bind to temporary object. So in C++11, you can write this:
int && z = 12; //C+11 only
Note that there is && intead of &. Also note that const is not needed anymore, even though the object which z binds to is a temporary object created out of integral-literal 12.
Since C++11 has introduced rvalue-reference, int& is now henceforth called lvalue-reference.
12 is a compile-time constant which can not be changed unlike the data referenced by int&. What you can do is
const int& z = 12;
Non-const and const reference binding follow different rules
These are the rules of the C++ language:
an expression consisting of a literal number (12) is a "rvalue"
it is not permitted to create a non-const reference with a rvalue: int &ri = 12; is ill-formed
it is permitted to create a const reference with a rvalue: in this case, an unnamed object is created by the compiler; this object will persist as long as the reference itself exist.
You have to understand that these are C++ rules. They just are.
It is easy to invent a different language, say C++', with slightly different rules. In C++', it would be permitted to create a non-const reference with a rvalue. There is nothing inconsistent or impossible here.
But it would allow some risky code where the programmer might not get what he intended, and C++ designers rightly decided to avoid that risk.
References are "hidden pointers" (non-null) to things which can change (lvalues). You cannot define them to a constant. It should be a "variable" thing.
EDIT::
I am thinking of
int &x = y;
as almost equivalent of
int* __px = &y;
#define x (*__px)
where __px is a fresh name, and the #define x works only inside the block containing the declaration of x reference.
Consider the below.
#include <string>
using std::string;
string middle_name () {
return "Jaan";
}
int main ()
{
string&& danger = middle_name(); // ?!
return 0;
}
This doesn't compute anything, but it compiles without error and demonstrates something that I find confusing: danger is a dangling reference, isn't it?
Do rvalue references allow dangling references?
If you meant "Is it possible to create dangling rvalue references" then the answer is yes. Your example, however,
string middle_name () {
return "Jaan";
}
int main()
{
string&& nodanger = middle_name(); // OK.
// The life-time of the temporary is extended
// to the life-time of the reference.
return 0;
}
is perfectly fine. The same rule applies here that makes this example (article by Herb Sutter) safe as well. If you initialize a reference with a pure rvalue, the life-time of the tempoary object gets extended to the life-time of the reference. You can still produce dangling references, though. For example, this is not safe anymore:
int main()
{
string&& danger = std::move(middle_name()); // dangling reference !
return 0;
}
Because std::move returns a string&& (which is not a pure rvalue) the rule that extends the temporary's life-time doesn't apply. Here, std::move returns a so-called xvalue. An xvalue is just an unnamed rvalue reference. As such it could refer to anything and it is basically impossible to guess what a returned reference refers to without looking at the function's implementation.
rvalue references bind to rvalues. An rvalue is either a prvalue or an xvalue [explanation]. Binding to the former never creates a dangling reference, binding to the latter might. That's why it's generally a bad idea to choose T&& as the return type of a function. std::move is an exception to this rule.
T& lvalue();
T prvalue();
T&& xvalue();
T&& does_not_compile = lvalue();
T&& well_behaved = prvalue();
T&& problematic = xvalue();
danger is a dangling reference, isn't it?
Not any more than if you had used a const &: danger takes ownership of the rvalue.
Of course, an rvalue reference is still a reference so it can be dangling as well. You just have to bring the compiler into a situation where he has to drag the reference along and at the same time you just escape the refered-to value's scope, like this:
Demo
#include <cstdio>
#include <tuple>
std::tuple<int&&> mytuple{ 2 };
auto pollute_stack()
{
printf("Dumdudelei!\n");
}
int main()
{
{
int a = 5;
mytuple = std::forward_as_tuple<int&&>(std::move(a));
}
pollute_stack();
int b = std::get<int&&>(mytuple);
printf("Hello b = %d!\n", b);
}
Output:
Dumdudelei!
Hello b = 0!
As you can see, b now has the wrong value. How come? We stuffed an rvalue reference to an automatic variable a into a global tuple. Then we escaped the scope of a and retrieve its value through std::get<int&&> which will evaluate to an rvalue-reference. So the new object b is actually move constructed from a, but the compiler doesn't find a because its scope has ended already. Therefore std::get<int&&> evaluates to 0 (although it is probably UB and could evaluate to anything).
Note that if we don't touch the stack, the rvalue reference will actually still find the original value of object a even after its scope has ended and will retrieve the right value (just try it and uncomment pollute_stack() and see what happens). The pollute_stack() function just moves the stack pointer forward and back while writing values to the stack by doing some io-related stuff through printf().
The compiler doesn't see through this though at all so be aware of this.