Rvalue to lvalue conversion and "named-refs-are-lvalues" rule - c++

There're lots of rvalue related quiestion but I didn't found answers to these exact questions.
I can't wrap my mind around "named reference is a lvalue reference" rule of thumb.
It seems really strange -- we declare the reference as an rvalue but since we have to somehow use this reference (otherwise, what's the point?), we name it and since it's named it's an lvalue after all!
Consider this code:
int&& foo(int&& arg)
{
arg = 10;
return arg; // ERROR, `arg` is an lvalue since it's named!
}
foo(1);
The questions are:
When exactly does arg become an lvalue?
What would be the type of arg if the function was void and b) and c) lines were not present?
Lot's of articles (just citing first found result) say that there may be implicit lvalue to rvalue conversion but the opposite direction is not possible -- why? This example shows that arg -s converted from int&& to int& and then tries to implicitly convert int& to int&& which causes compilation error -- just the opposite behaviour! That's why we need std::move which is basicaly an explicit static_cast to rvalue type.

arg the variable has type int&& and no value category.
arg the expression (it is an expression at lines 3 and 4) has type int and value category "lvalue"
Lvalue to rvalue conversion changes the value category of an expression, without changing its type. If you write arg+1 inside the function, the lvalue expression arg of type int would undergo this conversion to produce a prvalue expression of type int, since that's what built-in + requires.
There are no "lvalue to rvalue" or reverse conversions between int& and int&& because expressions never have reference types. The error in your program is a failure to bind an rvalue reference (of type int&&) to an lvalue expression (of type int).

Related

What is the type of an rvalue reference parameter inside a function? [duplicate]

This question already has answers here:
Why are rvalues references variables not rvalue?
(3 answers)
Closed 1 year ago.
I have a really simple function definition here:
void testRvalue(int&& r)
{
printf("rvalue ref is called\n");
testRvalue(r); // this line gives "no known conversion from 'int' to 'int &&' for 1st argument"
}
I mean inside this function, what is the type of r? Isn't it int&& (the parameter is int&& r so how come r is not of type int&&?) If so why can't I pass this r to this function itself, which takes a int&& type as parameter?
What is the type of r exactly? int&& or int? If int, why?
A very good read is Value categories.
Yes, the type of the variable r is indeed int&&. However what matters here is the expression and:
Each C++ expression (an operator with its operands, a literal, a
variable name, etc.) is characterized by two independent properties: a
type and a value category. Each expression has some non-reference
type, and each expression belongs to exactly one of the three
primary value categories: prvalue, xvalue, and lvalue.
The expression r is an lvalue:
lvalue
The following expressions are lvalue expressions:
the name of a variable [...], regardless of type. Even if the variable's type is rvalue reference, the expression consisting of its
name is an lvalue expression;
Rvalue references can bind to prvalues or xvalue but not to lvalues so if you want to bind an rvalue reference to r you need to convert r to an xvalue. This is done with std::move which despite its name is just an cast.
You can easily reason about this like so: if it has a name then it's an lvalue (even if the type of that id is rvalue reference). You can't bind rvalue references (which in principle should bind to temporary objects) to something that has a name. Something that has a name can be reused. You need std::move to enable move from that lvalue.
Regarding the message which says "no known conversion from 'int'". As shown above the type of the expression r is int, however a more appropriate diagnostic message would have been something along the lines: "rvalue reference cannot bind to lvalue".
And indeed newer clang and gcc give better messages:
gcc
error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'
clang
candidate function not viable: expects an rvalue for 1st argument

When a function takes an rvalue reference, what is the type of that variable within the function?

This is a question of terminology. If I have this:
#include <vector>
void g(std::vector<int>&& arg);
void f0(std::vector<int>&& v) {
static_assert(std::is_same<decltype(v), std::vector<int>&&>::value); // Looks like v is an rvalue reference.
static_assert(std::is_same<decltype((v)), std::vector<int>&>::value);
static_assert(std::is_same<std::decay<decltype(v)>::type, std::vector<int>>::value);
return g(std::move(v)); // Fine.
}
then what type is v? If you are talking about calling f0, you'd say "f0 takes an rvalue reference" (right?) but within f0, v isn't an rvalue reference, or else the std::move wouldn't be required? Right? But the static_assert showed that it is an rvalue, right?
Similarly:
void f1(std::vector<int>&& v) {
static_assert(std::is_same<decltype(v), std::vector<int>&&>::value);
static_assert(std::is_same<decltype((v)), std::vector<int>&>::value);
static_assert(std::is_same<std::decay<decltype(v)>::type, std::vector<int>>::value);
return g(v); // Error: cannot bind rvalue reference of type 'std::vector<int>&&' to lvalue of type 'std::vector<int>'.
// So is v just a std::vector<int>?
}
Local rvalue references act the same way:
void f2(std::vector<int>&& v) {
std::vector<int>&& vv = std::move(v);
static_assert(std::is_same<decltype(vv), decltype(v)>::value, "They are the same decltype. So being an argument isn't magic.");
static_assert(std::is_same<decltype(vv), std::vector<int>&&>::value);
static_assert(std::is_same<decltype((vv)), std::vector<int>&>::value);
static_assert(std::is_same<std::decay<decltype(vv)>::type, std::vector<int>>::value);
return g(vv); // Error: cannot bind rvalue reference of type 'std::vector<int>&&' to lvalue of type 'std::vector<int>'
}
What is the correct terminology to describe the type of v? Is it correct to say f0 takes an rvalue reference? If v is an rvalue reference, what's the terminology to say that an rvalue reference can't be used to call a function taking an rvalue reference?
The declared type of the variable named v is std::vector<int>&&. This type is read "rvalue reference to std::vector".
The name v can appears in an expression. Expressions never have reference type [expr.type]/1. But expressions have a value category. When the name v appears in an expression as in v[0], the subexpression v has type std::vector<int> and its value category is lvalue. This is the case of almost all id-expression (expressions that are just a name).
decltype(v) gives the declared type of the variable v.
decltype(expression) gives :
a lvalue-reference to the type of expression if expression is a lvalue,
a rvalue-reference to the type of expression if expression is a xvalue,
the type of expression if expression is a prvalue.
More details are given in [dcl.dcl]/1.
You're confusing types with value categories which, in your defence, is extraordinarily easy to do.
Yes, the function takes an argument of type "rvalue reference to std::vector<int>". This reference can be initialised from an rvalue expression of type std::vector<int>, at the callsite.
The type of the expression v inside the function, when you start to try to use it, is not std::vector<int>&&; the reference sort of "decays". That's just part of the machinery of how references work. (The decltype there is a bit of an oddity in this regard.) For all intents and purposes, you end up with an lvalue expression of type std::vector<int>. Anyway, at this point the type is sort of irrelevant; the point is that the name v is an lvalue, and to make it into an rvalue again you need std::move.
But the static_assert showed that it is an rvalue, right?
Nope. "rvalue reference" describes kinds of types. "rvalue" is a value category. You might wonder why they chose such confusing terminology. So do I.

What is the difference between rvalue reference and xvalue?

I'm new to C++ and this is my first question here so bear with me please ... I have been reading about lvalue and rvalue for a while and I think I understand most of it but there is bit that still confuses me ... so my question will be specific
rvalue references are considered lvalue (this part I understand) but functions that return rvalue references are considered rvalue (or xvalue to be specific) for instance:
int x = 32;
int& Lref = x; // Lref is lvalue ... ok
int& funcA(); // calling funcA() is lvalue ... ok
int&& Rref = 32; // Rref is lvalue ... ok I got this
int&& funcB(); // calling funcB() is rvalue ... Why?
So the question is: why calling funcB() which return rvalue reference is considered rvalue ?
Thanks in advance.
To answer the titular question, "rvalue reference" is a kind of type, while "xvalue" is a kind of expression.
rvalue references are considered lvalue (this part I understand)
They are not. Rvalue references are types, types are not expressions and so cannot be "considered lvalue". What you're referring to is the fact that if an expression consists solely of the name of a variable of type T&&, then it is an lvalue expression of type T.
why calling funcB() which return rvalue reference is considered rvalue
The most straightforward answer would be "by definition". A function call expression where the function's return type is T&& is an xvalue expression of type T. As for motivation, this is exactly what makes std::move do what it does: imbue any expression with the ability to be moved from (also known as "rvalue category" - see http://en.cppreference.com/w/cpp/language/value_category ).
Generally, "object" returned by a function are created on the stack part associated to the function itself. That is it, the value returned have to be copied (or moved) to a new object and this object is an rvalue.
In your code, you did a mistake for the lref. 32 is a rvalue reference.
To be simple, lvalue reference are object that we can obtain an address. We can't get the address of a rvalue reference.
int a = 50;
int &b = a; // Ok because we can get the address of a
int &c = 50; // Error we can't get the address of 50
int &&d = 50; // It is ok
And it works as well with "object".
Take for example a code with an unique_ptr which is not copyable.
std::unique_ptr<int> foo() {
auto ptr = std::make_unique<int>(5);
return ptr;
}
auto a = foo(); // is correct, the ptr will be moved because it is a rvalue
This function must return an rvalue to be correct. (Normally, the compiler when you don't specifize if it a lvalue or rvalue will use an rvalue reference).
Maybe you could take a look at :
http://en.cppreference.com/w/cpp/language/value_category
If it is unclear, let me know

IUnknown pointer reference

Why is the Visual Studio compiler happy with
void fn(int *&i)
{
;
}
and
void fn(IUnknown *const &p)
{
;
}
but not
void fn(IUnkown *&p)
{
;
}
where calling it looks like
IDXGIFactory *df = nullptr;
// init df
fn(df);
compiler error is
3 IntelliSense: a reference of type "IUnknown *&" (not const-qualified) cannot be initialized with a value of type "IDXGIFactory *" c:\Users\Carl\Documents\Visual Studio 2013\Projects\Project1\Project5\main.cpp 29 10 Project5
The closest thing I've dug up with research is that the compiler will only do one type conversion at a time, but that can't be right because then the const & version should break from doing a type and const conversion; however it is the & version that actually won't compile.
A non-const lvalue reference (like IUnknown*&) can only bind to an lvalue; it cannot bind to an rvalue. A const-qualified lvalue reference (like IUnknown* const&) can bind to an rvalue.
It's easier to consider a simpler case that does not involve pointers or function calls:
int i = 0;
double x = i; // (1) Well-formed
double const& y = i; // (2) Well-formed
double& z = i; // (3) Ill-formed
Here, i is an object of type int. When i is used in an expression, it is an lvalue.
In (1), we initialize the object x (of type double) from i (of type int). The type does not match, but this is okay, because there is an implicit conversion from int to double. The "result" of this conversion is an rvalue expression(*) of type double, which is used to initialize x.
In (2), we initialize the const-qualified reference y (of type double const&) from i. Again, the types do not match, so the implicit conversion is used to convert the int to double. The "result" of this conversion is an rvalue. As noted at the beginning, a const-qualified reference can bind to an rvalue, so y is bound to the "result" of the conversion.
In (3), we attempt to initialize the non-const reference z (of type double&) from i. The types do not match, so a conversion would be required. The conversion cannot be used here, because the "result" of the conversion is an rvalue, and as noted at the beginning, a non-const reference cannot bind to an rvalue.
C++ has special rules to permit const lvalue references to bind to rvalue expressions. You can find out why from other questions here on StackOverflow, like "How come a non-const reference cannot bind to a temporary object?"
Your case is exactly the same as this one: the type of your argument (IDXGIFactory*) is not the same as the type of the parameter (IUnknown* or a reference thereto), so an implicit conversion is required to convert the argument to the parameter type (in this case, it's a conversion from a pointer to a derived class to a pointer to a base class). The "result" of this conversion is an rvalue expression, however, so it cannot bind to the non-const reference IUnknown*&.
(*)It's really a prvalue; I've used the C++98 expression taxonomy in this answer for simplicity. See this question for information about the C++11 value categories.

binding a lvalue expression of type T&&

In the last few days I've been trying to grasp an apparently trivial principle behind lvalue/rvalue references. Let us define a new rvalue reference:
int&& x = 12;
x is therefore an lvalue expression of type int&&. Since x is a lvalue, it can be bound to a lvalue reference of the same type, i.e., a lvalue reference of type int&&. Such a lvalue reference would be defined as:
int&& & ref_x = x; // non-working code, just for the sake of explanation
Of course, it is not possible to explicitly define a reference to a reference, and the correct way to perform the binding is as follows:
int& ref_x = x;
C++ Primer reports the following about using references as initializers:
when we use a reference as an initializer, we are really using the
object to which the reference is bound
On the other hand, the lvalue reference must match the type of the lvalue expression. What am I missing? Is reference collapsing involved in this case?
Thanks.
No, x (as an expression) is an expression of type int. The type of the value of an expression is never a reference. In fact, x is also an lvalue, since it is a named thing.
Also, there are no references to references, for the same reason: References bind to values, and values are never references.
If you're ever confused, just keep telling yourself: The value of an expression is always an object type. Whether the value category of an expression is l or r only determines what sort of things the value can bind to; it has no effect on its type.