I watched Scott Meyers's extremely informative video on Universal References, in which I learned most of what I know about Rvalue references, moving, and forwarding. At one point he was talking about rvalueness as opposed to the type of a variable, and he said something to the effect of "rvalueness is independent of type".
I understand that you can have a method like this:
void func(MyType&& rRef)
{
// Do domething with rRef...
}
and that here rRef is an lvalue because it can be identified, its address can be taken, etc., even though its type is MyType&&.
But an rvalue cannot be any type, can it? I mean, it can only be a MyType&&, right? In that sense I thought type is not entirely independent of rvalueness. Maybe I'm missing something.
Updated: My point can be made clearer like this. If in func() I call one of two overloaded functions defined as
void gunc(MyType&& rRef)
{
// ...
}
void gunc(MyType& lRef)
{
// ...
}
i.e. either by calling gunc(std::move(rRef)) or gunc(rRef), it seems that the type of the resulting expression between parenthesis is not independent of rvalueness.
The type of an expression does not have any traces of references. So if for a moment we assume that references could have reference type, then we would have the following
int a = 0;
int &ra = a;
int c = a + 42;
int d = ra + 42;
In the above, the expression a would have type int, and the expression ra would have type int&. I think that in nearly all the rules of the spec that relate expressions to type, for example rules that say "expression E must be of type X", we would have to add "... or reference to type X" (think about cast operators). So my educated guess is that this would be too much of a burden to be useful.
C++ has the following types
static type of an expression
Just called "type of the expression" (if not otherwise specified that the dynamic one is meant). This is a property of expressions that designate the type of expressions abstracted away of what the expression refers to at compile time. For example if a refers to an int& or int variable, or is a literal 0, all those expression have type int.
dynamic type of an lvalue expression
This is the type that the non-base-class object that an lvalue expression refers to has.
ofstream fs("/log");
ostream &os = fs;
In this, os has the static type ostream and the dynamic type ofstream.
type of an object or reference
This is the type that an object or reference actually has. An object always has a single type and its type never changes. But what object exists at what location is something only known at runtime, so generally, "type of an object" is a runtime thing too
ostream *os;
if(file)
os = new ofstream("/log");
else
os = new ostringstream;
The type of the object denoted by *os (and the dynamic type of the lvalue *os aswell) is known only at runtime
int *p = new int[rand() % 5 + 1];
Here, the type of the array that was created by operator new is only known at runtime too, and (thanksfully) does and can not escape to the static C++ type system. The infamous aliasing rule (that forbids reading objects from incompatible lvalues, roughly speaking) speaks of "dynamic type" of objects, presumably because it wants to highlight that runtime concerns are of interests. But strictly speaking, saying "dynamic type" of an object is weird, because an object doesn't have a "static type".
declared type of a variable or member
This is the type that you gave in a declaration. In relation to the type of an object or the type of an expression, this sometimes can be subtly different
struct A {
A() { }
int a;
};
const A *a = new const A;
volatile const A *va = a;
Here, the expression a->a has type const int, but the declared type of the member that a->a resolves to has type int (the member entity). The type of the object denoted by a->a has type const int, because we created a const A object with the new expression, and therefore all non-static data members are implicitly const subobjects. In va->a, the expression has type volatile const int a, and the declared type of the variable still has type int and the type of the object referred to still has type const int.
When you say "type of a" and you declared "a" as "int &a;" you therefor always have to say what you mean by "type of a". Do you mean the expression? Then "a" has type int. It can even become nastier. Imagine "int a[10];". Here the expression "a" has type int* or int[10] depending on whether you consider the array to pointer conversion to have taken place in your expression or not, when you ask for the "type of a". If you ask for the type of the variable referred to by "a", then the answer uniquely is int and int[10] respectively.
So what type can an rvalue be of? Rvalues are expressions.
int &&x = 0;
int y = std::move(x);
int z = x;
Here, we have rvalues 0, and std::move(x). Both rvalues have type int. The expression x appearing in the initializer for z is an lvalue, even though it refers to the same object that the rvalue std::move(x) refers to.
Your last point in your question about the overloaded function called with an rvalue or lvalue respectively is interesting. Just because rvalue references are written as int && does not mean that rvalues have type int. They are called rvalue references because you can initialize them with rvalues and the language prefers that initialization over initializing an lvalue reference with an rvalue.
Also, it may be useful to see expressions in name-form that are rvalues
enum A { X };
template<int Y> struct B { };
If you use X or Y, they are rvalues. But those cases are the only one that I can think of.
I think you are leaving off part of his quote:
A final point is worth bearing in mind: the lvalueness or rvalueness
of an expression is independent of its type.
He explains it here. His main point was this:
The type of an expression does not tell you whether it is an lvalue or
an rvalue.
And in his concluding remarks:
In a type declaration, “&&” indicates either an rvalue reference or a
universal reference – a reference that may resolve to either an lvalue
reference or an rvalue reference. Universal references always have the
form T&& for some deduced type T.
Reference collapsing is the mechanism that leads to universal
references (which are really just rvalue references in situations
where reference-collapsing takes place) sometimes resolving to lvalue
references and sometimes to rvalue references. It occurs in specified
contexts where references to references may arise during compilation.
Those contexts are template type deduction, auto type deduction,
typedef formation and use, and decltype expressions.
The type T is used here to mean any type. It could be int&&, or double&&, or Widget&& - it doesn't matter.
First, let's limit the discussion to plain rvalue references and leave universal references aside. We're not talking about template <typename T> ... T &&var ...
As far as regular Type &&var = somevalue; case of rvalue reference, I think the meaning of it is this:
Whatever is bound to this reference was "disposable" when it was bound to the reference. It was going out of scope. If you modified it at the moment you bound it, no one would ever know. There were no other references to it at the time it was bound.
This allows us to take some liberties with rvalue references that we cannot take with other kinds of variables. The first use that comes to mind is to steal its contents with swap().
Related
I know that a named reference is an lvalue:
int x = 1;
int& ref1 = x;
int&& ref2 = std::move(x);
I've read the explanation — that is because we can take the address of those ref1 and ref2.
But when we take the address of a reference we actually take the address of the referenced object, don't we? So this explanation doesn't seem to be correct.
So why a named reference is an lvalue?
Per [expr.prim.id.unqual] (8.1.4.1 Unqualified names):
[...] The expression is an lvalue if the entity is a function,
variable, or data member and a prvalue otherwise; it is a bit-field if
the identifier designates a bit-field ([dcl.struct.bind]).
Per [basic]/6:
A variable is introduced by the declaration of a reference other
than a non-static data member or of an object. The variable's name, if
any, denotes the reference or object.
The declaration
int&& ref2 = std::move(x);
is a "declaration of a reference other than a non-static data member." Therefore, the entity denoted by ref2 is a variable. So the expression ref2 is an lvalue.
That explanation is just a simplification. lvalues aren't defined by being "something you can take the address of", but by a specific set of rules about the value category of expressions. Those rules are carefully constructed so as to result in a self-consistent language in which everything fits together reasonably neatly.
That being said, the explanation does rather fit here, if you consider that by writing ref1, you're not really naming "the reference" but the thing being referred to. That's the magic of references: you're supposed to consider them name aliases rather than entities in their own right.
There are some abstraction leaks surrounding this (particularly, member references), but that's the gist.
You ought to forget about notions like "the reference is an lvalue" and instead think about expressions. Objects have types; expressions have value categories.
Here is an explanation from Scott Meyers's book "Effective Modern C++":
In fact, T&& has two different meanings. One is rvalue reference, of course. Such references behave exactly the way you expect: they bind only to rvalues, and their primary raison d’être is to identify objects that may be moved from.
void f(Widget&& param); // rvalue reference
Widget&& var1 = Widget(); // rvalue reference
auto&& var2 = var1; // not rvalue reference
template<typename T>
void f(std::vector<T>&& param); // rvalue reference
template<typename T>
void f(T&& param); // not rvalue reference
The other meaning for T&& is either rvalue reference or lvalue reference. Such references look like rvalue references in the source code (i.e., T&&), but they can behave as if they were lvalue references (i.e., T&). Their dual nature permits them to bind to rvalues (like rvalue references) as well as lvalues (like lvalue references). Furthermore, they can bind to const or non-const objects, to volatile or non-volatile objects, even to objects that are both const and volatile. They can bind to virtually anything. Such unprecedentedly flexible references deserve a name of their own. I call them universal references.
While not the actual standard, I am relying this page on cppreference.com for this specific verbiage:
An lvalue is an expression that identifies a non-temporary object or a
non-member function.
The following expressions are lvalues:
The name of a variable or function in scope, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue
reference, the expression consisting of its name is an lvalue
expression.
...
My (simplified) comprehension of the quoted section above is that an lvalue:
Is a reference
Must not be a temporary object
I know that references are not objects, so point #2 must mean that in terms of a reference, it must not refer to a temporary object. However, from the expression itself, does that mean a reference to a temporary is not an lvalue? You can have references to temporary and non-temporary objects:
int myvar = 0;
int& ref_myvar = myvar; // Reference to non-temporary
class foo {};
foo const& ref_foo = foo{}; // Reference to temporary
In the above code snippet, usage of ref_foo in a separate expression later would be an lvalue or not? There is a rule for rvalue references that states that rvalue references used by name in an expression are still lvalues (because you refer to the name at that point). Does this rule also apply to lvalue references (since they also have names and using the name itself in an expression would make it an lvalue, as it does for rvalue reference variables)?
I hope I'm making some sort of sense. At least I hope the source of my confusion is evident. Some examples of how the lvalue references above would be used in an expression to prove some points relevant to my question would be a huge help as well.
I'd say that the cppreference wording is OK for a "general introduction"-level discussion or even for "most everyday uses"-level discussion. However, once you get into the fine technical details, such statements can become somewhat misleading.
The important point is that the value category ("being an lvalue") is the property of an expression, not of an object. You can have a temporary object accessed through an lvalue, and you can have a non-temporary object accessed through an rvalue.
To refer to your examples:
ref_myvar and ref_foo are both lvalues, and always will be, regardless of how you use them. In the following:
foo&& rref = foo{};
rref is, and always will be, an lvalue as well. It is a reference to an rvalue, but the reference itself has a name and so is an lvalue.
If you want to treat an lvalue as an rvalue, you use the standard-provided case operator for that:
rvalue = std::move(lvalue);
Let's analyse this code:
int someint = std::move(ref_myvar);
ref_myvar is an lvalue. std::move(ref_myvar) is an rvalue. someint is an lvalue.
I don't think there is a concise way to define an lvalue without going full standardese, but name (or absence thereof) plays an important part in most definitions. I'll try my hand at such a definition; these are lvalues:
An expression which is a name, except for enumerators and member functions.
An expression of type "lvalue reference to something."
The result of dereferencing a pointer.
Notice that ref_myvar, ref_foo, and rref are all lvalues because they have a name. std::move(ref_myvar) doesn't have a name, and so it's an rvalue.
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.
I'm learning about rvalue references, and the tutorial told me this:
X foo();
X x;
x = foo();
Rather obviously, it would be ok, and much more efficient, to swap resource pointers (handles) between x and the
temporary, and then let the temporary's destructor destruct x's original resource.
In other words, in the special case where the right hand side of the
assignment is an rvalue, we want the copy assignment operator to act
like this.
So, does this mean that return values from functions are always constant by default, and thereby an rvalue? If yes: Are they always constant, or are there exceptions too?
Rvalue-ness and constant-ness are not synonyms, but rather a bit orthogonal. With the following definitions:
struct X {};
const X x;
const X f();
int X();
We can categorize the following expressions:
x; // constant lvalue
f(); // constant rvalue
g(); // non-constant rvalue
As of your particular question: no, not all rvalue expressions are constant.
So, does this mean that return values from functions are always constant by default, and thereby an rvalue? If yes: Are they always constant, or are there exceptions too?
No. They are rvalues iff they don't return a reference type (cv T& or cv T&&). They are constant iff their return type is const-qualified.
That means a return value from a function X foo() is an rvalue (prvalue, if you want new standardese), and not a constant. Moreover, in an expression like x = foo(), we usually don't care if the temporary changes during the assignment which is pretty much the idea behind move-semantics.
§5.2.2/10 (in N3225) states:
A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
You might be confusing types, objects and expressions. Only expressions have a notion of lvalue/rvalueness. The expression foo(); is an rvalue of type X. As such, the statement x = foo(); will invoke -- if possible -- the member function X::operator=(X &&) of x. Failing that, it will bind to the standard X::operator=(X const &), since rvalues bind to const-references.
Note that it is possible in theory to have constant rvalues, for example if you had a function declared as X const bar();. Then bar() would not bind to X&&, but only to X const && (as well as to X const &). There is no use for this in practice, though.
See this previous question, which tells us that neither are rvalue expressions neither implicitly of a const type, nor are the objects they represent made inherently immutable.
However, it is undefined (or forbidden — I forget which) in some cases to modify an object through an rvalue. This does seem to yield a sort of conditional inherent immutability to objects accessed through an rvalue, and the result of evaluating a function call is often — though not always! — an rvalue expression.
C++03 §4.2 N°1:
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to an rvalue of type “pointer to T.” The result is a pointer to the first element of the array.
What has been confusing in this statement for a long time for me was that I didn't quite understand what an rvalue of array type would mean. That is, I couldn't come up with an expression whose type were an array and the result were an rvalue. I read this thread, which basically asks the same question and the accepted answer is "no, there is no rvalue of array type". I think I just might have a contradiction to this.
C++03 §5.2.5 N°4: (is about expression E1.E2)
If E2 is a non-static data member, and the type of E1 is “cq1 vq1 X”, and the type of E2 is “cq2 vq2 T”,the expression designates the named member of the object designated by the first expression. If E1 is an lvalue, then E1.E2 is an lvalue.
I assume that otherwise it is an rvalue (provided E2 is not a reference, that case is covered by §5.2.5 N°3) and therefore...
struct A
{
int a[4];
};
A f()
{
A a;
return a;
}
int main()
{
f().a; //I think this is an rvalue of array type...
}
I see two options here:
Option1: I am correct, hurray, yay, cool. In this case the question is: are there other examples?
Option2: I am incorrect, in this case the question is: is this a defect of the standard?
I don't know about 1, but I really doubt about 2 because when they speak about function-to-pointer conversions they mention just lvalues of function types (obviously appreciating that there are no rvalues of such). So it's very likely they had thought abour rvalues of array types.
So, basically my question is whether or not I have come up with an example of rvalue of array type, and if not, please provide a valid one, which I stongly believe there exists.
Yes, you are correct. The expression is an rvalue of array type. This is not a defect - the committee knows about it, and it was also a common issue in C89, which only allows conversion to pointers for lvalues of array types. As a consequence, you could not index or dereference an array like f().a. C99 fixed this, and C++ does not have a problem with it.
Note that whether or not it is an rvalue is independent to whether or not the expression denotes an object. C++03 accidentally omitted to say that an rvalue expression that is of array type denotes an object. This was fixed in C++0x by DR#450.
(obviously appreciating that there are no rvalues of such)
There are actually rvalues of function types. These occur for non-static member functions denoted by a class member access expression
struct A { void f(); };
/* A().f is an rvalue of type "void()" */
int main() { A().f(); }
The A is an rvalue. The array inside it isn't. Imagine the case in which you have a method chain on that temporary object - the variables within it live for more than one method call and return, and they may pass references (valid for the duration of the chain) to other functions. Those functions cannot know in advance that they shall be called upon an rvalue.
In the latest version of the draft, you can overload functions on rvalue/lvalue *this. However, even then, an rvalue reference does not make the contents of what is referred to an rvalue, and I'm not entirely sure that ANY compiler currently supports this, and I know that MSVC doesn't.
Infact, using decltype, you can determine easily that the compiler calls that array an lvalue.
Consider:
template<typename A, typename B> auto sum(A&& a, B&& b) -> decltype(std::forward<A>(a) + std::forward<B>(b)) {
return std::forward<A>(a) + std::forward<B>(b);
}
That's what decltype was for, and it most definitely differentiates between lvalues and rvalues. Alternatively, consider this:
int main()
{
auto var = f().a;
}
Var is an int*. That's an instant fail, since f().a immediately dies. Not sure of my immediate opinion on that, but it's certainly not valid for an rvalue.