I know what 'xvalues', 'prvalues', 'rvalues' and 'lvalues' are, how they are helpful and I've seen real examples of them. But I've never understand what a 'glvalue' is, and how it co-operate with the others. I've searched everywhere but with no-luck even in the latest standard paper it was barely noticed. Can somebody explains it to me and show some examples?
Note that this is not a duplicate of this, as even there nobody gave an example of 'glvalue'. Here too. It was only barely mentioned like this:
A glvalue (“generalized” lvalue) is an lvalue or an xvalue.
A glvalue is anything that isn't a prvalue. Examples are names of entities, or expressions that have reference type (regardless of the kind of the reference).
int i;
int* p = &i;
int& f();
int&& g();
int h();
h() // prvalue
g() // glvalue (xvalue)
f() // glvalue (lvalue)
i // glvalue (lvalue)
*p // glvalue (lvalue)
std::move(i) // glvalue (xvalue)
As the quote in your question clearly states, the category glvalue includes all xvalues and lvalues. lvalues, xvalues and prvalues are complementary categories:
Every expression belongs to exactly one of the fundamental
classifications in this taxonomy: lvalue, xvalue, or prvalue.
You should be familiar with lvalues. Now consider what xvalues are, [expr]/6:
[ Note: An expression is an xvalue if it is:
the result of calling a function, whether implicitly or explicitly, whose return type is an rvalue reference to object type,
a cast to an rvalue reference to object type,
a class member access expression designating a non-static data member of non-reference type in which the object expression is an
xvalue, or
a .* pointer-to-member expression in which the first operand is an xvalue and the second operand is a pointer to data member.
[…] — end note ]
So, roughly speaking, you could think of glvalues as "All lvalues plus expressions involving rvalue references".
We use it to describe expressions that refer to objects rather than "being" those objects.
By definition from §3.10\1
A glvalue (“generalized” lvalue) is an lvalue or an xvalue
where
Every expression belongs to exactly one of the fundamental classifications in this taxonomy: lvalue,
xvalue, or prvalue.
Here the taxonomy:
So, for instance, every lvalue is a glvalue:
int x = 7; // x is an lvalue. x is also a glvalue.
// 7 is a literal, so it is a prvalue. 7 is not a glvalue.
auto foo = static_cast<int&&>(x); // foo is an lvalue, so it is a glvalue
// the cast is an rvalue but not a prvalue,
// it is an xvalue. so it is a glvalue.
Related
I wanted a clarification regarding C++ value categories.
struct Foo {...};
void do_something(Foo{});
Is the Foo{} above a r-value or an x-value?
I understand that there is a hierarchy of value categories, and that r-values are actually either an x-value or a pr-value. I also know that the standard says that "temporary materialization is an x-value" but I wasn't sure if the creation of a temporary fits under this definition.
But what I wasn't sure about was whether gl-value and r-value were "abstract" categories in the hierarchy, with the leafs (l-value, x-value, and pr-value) being the actual implementations.
Could someone explain this for me?
From https://eel.is/c++draft/basic.lval (accessed 04/02/2021):
Every expression belongs to exactly one of the fundamental
classifications in this taxonomy: lvalue, xvalue, or prvalue. This
property of an expression is called its value category.
This answers your question of whether gl-value and r-value are categories of values: they are.
On what kind of value your particular case is, let's look at xvalue:
An xvalue is a glvalue that denotes an object whose resources can be reused (usually because it is near the end of its lifetime).
[ ... snip ... ]
# [Note 3: An expression is an xvalue if it is:
(4.1) the result of calling a function, whether implicitly or explicitly, whose return
type is an rvalue reference to object type ([expr.call]),
(4.2) a cast to an rvalue reference to object type ([expr.type.conv],
[expr.dynamic.cast], [expr.static.cast] [expr.reinterpret.cast],
[expr.const.cast], [expr.cast]),
(4.3) a subscripting operation with
an xvalue array operand ([expr.sub]),
(4.4) a class member access
expression designating a non-static data member of non-reference type
in which the object expression is an xvalue ([expr.ref]), or
(4.5) a
.* pointer-to-member expression in which the first operand is an
xvalue and the second operand is a pointer to data member
([expr.mptr.oper]).
Your case does not appear to fit any of those. Now let's look at prvalue:
A prvalue is an expression whose evaluation initializes an object or computes the value of an operand of an operator, as specified by the context in which it appears, or an expression that has type cv void.
Your case appears to be initialising an object. For this reason, I would say it's a prvalue.
Look at this expression:
T t;
T& ref = t;
The ref expression is an lvalue or an rvalue? I believe it's an rvalue because ref does not "designates a function or an object":
"An lvalue (so called, historically, because lvalues could appear on
the left-hand side of an assignment expression) designates a function
or an object."
[open-std draft n3092]
According to cppreference, a reference is not a object.
"The following entities are not objects: value, reference [...]"
I'm in doubt, because ref is in the left side of =.
I'm in doubt, because ref is in the left side of =.
ref is a name and therefore an lvalue.
I believe it's an rvalue because ref does not "designates a function or an object"
It designates the same object as t does.
A reference does designate a function or object! It is literally written like that in the standard! See in [expr.type]/1:
If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis.
The expression designates the object or function denoted by the reference,[...]
The snippet below compiles
#include <iostream>
int& f() { static int i = 100; std::cout << i << '\n'; return i; }
int main()
{
int& r = f();
r = 101;
f();
}
and print the values (live example)
100
101
Now, reading §8.5.3/5 in N4140, I can see that it compiles because of bullet point (5.1.1), that is, the reference is an lvalue reference, the initializer expression is an lvalue and int is reference-compatible with int (or with int& - I don't know for sure which one I should use here).
Bullet points (5.1) and (5.1.1):
— If the reference is an lvalue reference and the initializer expression
— is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with
“cv2 T2,” or ...
Now suppose I change the left value reference in the declaration int& r = f(); by a right value reference, i.e., int&& r = f();. I know the code won't compile, as an rvalue reference doesn't bind to an lvalue. But what I'm curious is, how to reach this conclusion using the Standard?
I'll explain what are my difficulties:
Clearly int&& r = f(); is covered by bullet point (5.2), because the reference is an rvalue reference.
Bullet point (5.2):
— Otherwise, the reference shall be an lvalue reference to a
non-volatile const type (i.e., cv1 shall be const), or the reference
shall be an rvalue reference.
In principle, I would say that (5.2.1.1) supports this initialization as the initializer is a function lvalue and int is reference compatible with int (or with int&).
Bullet points (5.2.1) and (5.2.1.1):
— If the initializer expression
— is an xvalue (but not a bit-field), class prvalue, array prvalue or function
lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or ...
Edit
I've included the bullet points verbatim from N4140 (C++14), which are equivalent to similar bullet points in N3337 (C++11).
the initializer expression is an lvalue and int is reference-compatible with int (or with int& - I don't know for sure which one I should use here).
Reference-compatibility is a relation applied to the type referred to, not the reference type. For example, [dcl.init.ref]/5 talks about intializing "a reference to type cv1 T1 by an expression of type cv2 T2", and later compares e.g. "where T1 is not reference-related to T2".
The type of the expression f() is just int, despite the fact that the return type of f is int&. Expressions simply do not have reference type when we observe them(*); the reference is stripped and used to determine the value category (see [expr]/5). For int& f(), the expression f() is an lvalue; for int g(), the expression g() is an rvalue.
(*)To be perfectly precise, expressions can have reference type in the Standard, but only as the "initial" resulting type. The reference is dropped "prior to any further analysis", which implies that this referencess is simply not observable through the type.
Now suppose I change the left value reference in the declaration int& r = f(); by a right value reference, i.e., int&& r = f();. I know the code won't compile, as an rvalue reference doesn't bind to an lvalue. But what I'm curious is, how to reach this conclusion using the Standard?
The confusion, as it seems from the discussion in the comments, seems to be that f() is not a function lvalue. Value categories such as "lvalue" and "rvalue" are properties of expressions. The term "function lvalue" must therefore refer to an expression, namely an expression of function type with the value category "lvalue".
But the expression f() is a function call expression. Grammatically, it's a postfix-expression, the postfix being the function argument list. As per [expr.call]/10:
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.
And [expr.call]/3
If the postfix-expression designates a destructor [...]; otherwise, the type of the function call expression is the return type of the statically chosen function [...]
That is, the (observed see above) type of the expression f() is int, and the value category is "lvalue". Note that the (observed) type is not int&.
A function lvalue is for example an id-expression like f, the result of indirecting a function pointer, or an expression yielding any kind of reference to function:
using ft = void();
void f();
ft& l();
ft&& r();
ft* p();
// function lvalue expressions:
f
l()
r()
*p()
[expr.prim.general]/8 specifies that those identifiers like f are, as id-expressions, lvalues:
An identifier is an id-expression provided it has been suitably declared. [...] The type of the expression is the type of the identifier. The result is the entity denoted by the identifier. The result is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise.
Back to the example int&& r = f();. Using some post-N4296 draft.
[dcl.init.ref]
5 A reference to type “cv1 T1” is initialized by an expression of type
“cv2 T2” as follows:
(5.1) If the reference is an lvalue reference and the initializer expression
The reference is an rvalue reference. 5.1 does not apply.
(5.2) Otherwise, the reference shall be an lvalue reference to a
non-volatile const type (i.e., cv1 shall be const), or the reference
shall be an rvalue reference. [example omitted]
This applies, the reference is an rvalue-reference.
(5.2.1) If the initializer expression
(5.2.1.1) is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and [...], or
(5.2.1.2) has a class type (i.e., T2 is a class type) [...]
The initializer is an lvalue of type int. 5.2.1 does not apply.
(5.2.2) Otherwise:
(5.2.2.1) If T1 or T2 is a class type [...]
(5.2.2.2) Otherwise, a temporary of type “cv1 T1” is created and copy-initialized (dcl.init) from the initializer expression. The reference is then bound to the temporary.
Finally, 5.2.2.2 applies. However:
If T1 is reference-related to T2:
(5.2.2.3) cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2; and
(5.2.2.4) if the reference is an rvalue reference, the initializer expression shall not be an lvalue.
T1 and T2 are int (the reference of the return type of f() is removed and used only to determine the value category), so they're reference-related. cv1 and cv2 are both empty. The reference is an rvalue reference, and f() is an lvalue, hence 5.2.2.4 renders the program ill-formed.
The reason why the term "function lvalue" appears in 5.2.1.1 might be related to the problem of "function rvalues" (see, for example, N3010 - Rvalue References as "Funny" Lvalues). There were no function rvalues in C++03, and it seems the committee didn't want to introduce them in C++11. Without rvalue references, I think it's impossible to get a function rvalue. For example, you may not cast to a function type, and you may not return function types from a function.
Probably for consistency, function lvalues can be bound to rvalue references to function types via a cast:
template<typename T>
void move_and_do(T& t)
{
T&& r = static_cast<T&&>(t); // as if moved
}
int i = 42;
move_and_do(i);
move_and_do(f);
But for T being a function type like void(), the value category of static_cast<T&&>(t) is lvalue (there are no rvalues of function type). Hence, rvalue references to function types can bind to function lvalues.
int x = 8;
int y = x ;
Here how a lvalue can be act as rvalue ? I know this is a silly question , but i just want to make my concepts clear on rvalue and lvalue .
Through an lvalue to rvalue conversion.
You can use an lvalue almost anywhere where an rvalue is required and an implicit lvalue to rvalue conversion will occur automatically.
Informally this conversion is "evaluating" or "taking the value of" the object that the lvalue refers to. This isn't strictly true in all cases; in unevaluated contexts, such as the operand of sizeof, the value of the object won't be used.
ISO/IEC 14882:2011 4.1 [conv.lval]:
A glvalue (3.10) of a non-function, non-array type T can be converted to a prvalue. If T is an incomplete type, a program that necessitates this conversion is ill-formed. If the object to which the glvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior. If T is a non-class type, the type of the prvalue is the cv-unqualified version of T. Otherwise, the type of the prvalue is T.
An lvalue is any value that can appear on the left hand side of an assignment. Something that can be assigned to, like the variable x in this case. Rvalues are things that have a value, like 8 or getting the value of x. You can think of lvalues as a subset of rvalues, that is all lvalues can have their value queried and used as an rvalue.
I'm trying to understand the C++11 concepts.
The standard draft which I have says:
An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its
resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving
rvalue references (8.3.2). [ Example: The result of calling a function whose return type is an rvalue
reference is an xvalue. —end example ]
OK, so what exactly are the "certain kinds of expressions" that produce xvalues? This part of the spec does not detail a list of these expressions.
I understand lvalue and prvalue (at least I think, I understand).
There is a helpful non-normative note in the introduction to §5 (C++11 §5[expr]/6):
[ Note: An expression is an xvalue if it is:
the result of calling a function, whether implicitly or explicitly, whose return type is an rvalue reference to object type,
a cast to an rvalue reference to object type,
a class member access expression designating a non-static data member of non-reference type in which the object expression is an xvalue, or
a .* pointer-to-member expression in which the first operand is an xvalue and the second operand is a pointer to data member.
In general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not. —end note ]
Searching through the rest of §5, this list appears exhaustive. The list is followed by an example:
struct A {
int m;
};
A&& operator+(A, A);
A&& f();
A a;
A&& ar = static_cast<A&&>(a);
The expressions f(), f().m, static_cast<A&&>(a), and a + a are xvalues. The expression ar is an lvalue.
There are two common ways to get an xvalue expression:
Use std::move to move an object. std::move performs a static_cast to an rvalue reference type and returns the rvalue reference.
Use std::forward to forward an rvalue. std::forward is typically used in a function template to enable perfect forwarding of a function argument.
If the argument provided to the function template was an rvalue, the parameter type will be an rvalue reference, which is an lvalue. In this case, std::forward performs a static_cast to an rvalue reference type and returns the rvalue reference.
(Note: If the argument provided to the function template was an lvalue, the parameter type will be an lvalue reference and std::forward will return an lvalue reference.)
Clause 5, which describes the syntax of valid expressions, lists for each expression syntax the conditions in which the expression is an lvalue, an xvalue, or a prvalue. The complete list of possible xvalues from clause 5 is:
5.2.2 paragraph 10: A function call is ... an xvalue if the result type is an rvalue reference to object type.
(In the technical language of the Standard, "object type" doesn't mean the same as "class type". "Object type" includes fundamental types, pointers, and arrays, and excludes only function types. An rvalue reference to function type is always treated as an lvalue, not xvalue.)
The most notable functions which return an rvalue reference are of course std::move and sometimes std::forward.
5.2.5 paragraph 4: If E2 is a non-static data member ... if E1 is an xvalue, then E1.E2 is an xvalue
(On the other hand, a data member lookup E1->E2 is always an lvalue.)
Similarly, if E1 is an xvalue, then the data member lookup E1.*E2 is an xvalue:
5.5 paragraph 6: The result of a .* expression whose second operand is a pointer to a data member is of the same value category (3.10) as its first operand.
For the various types of casts:
dynamic_cast<Type>(expr): 5.2.7 paragraph 2
static_cast<Type>(expr): 5.2.9 paragraph 1
reinterpret_cast<Type>(expr): 5.2.10 paragraph 1
const_cast<Type>(expr): 5.2.11 paragraph 1
(Type) expr: 5.4 paragraph 1
the expression is an xvalue if and only if Type is an rvalue reference to object type. The same is also true for Type(expr), since
5.2.3 paragraph 1: If the expression list [in parentheses following a type name] is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4).
(On the other hand, Type{expr} is always a prvalue.)
Section 5.16 on the conditional operator ends up saying that A ? B : C can sometimes be an xvalue if B and/or C is an xvalue. But the complete rules are difficult to summarize.
If an expression ends up calling a user-defined overloaded operator function, then section 5.2.2 applies to that expression, not the one that describes the built-in operator's behavior. (See the expression a + a in the example #James posted.)
As you mentioned,
An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example).
Focus: object, be moved, end lifetime
Here is an example:
void move_test(){
std::string s = "I'm here!";
std::string m = std::move(s); // move from <s> to <m>
// s is now in an undefined, but valid state;
std::cout << "s=" << s << "; &s=" << &s << std::endl;
}
By moving, we can convert a named value (an lvalue,here is s) to being an rvalue(that is std::move(s)). More specifically, since it can’t be a prvalue (it has a name! In other words, it has an identity), it ends up being an xvalue.
xvalue always serves C++ move semantics.
What I gather from what I've read is that calling something an xvalue is a fancy way of saying:
An xvalue is just an rvalue whose storage may have been deallocated, so using it means you have to verify its existence by yourself.
Generally, it's one or more levels of indirection away from an actual rvalue.
By contrast, an rvalue is guaranteed to have its storage space existing as long as it is in scope.
I may be wrong but this is what I've understood.