What expressions create xvalues?
Asked Answered
G

4

30

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).

Gastronome answered 20/7, 2012 at 15:1 Comment(5)
A complete answer: What are rvalues, lvalues, xvalues, glvalues, and prvalues?Feminine
That part of the standard explains the general understanding of the term. It does not detail every kind of expression that generates an xvalue. That happens elsewhere in the standard.Burgrave
@NicolBolas : I had read that question and answers before and I decided to post another question due to this reason exactly.Gastronome
@PermanentGuest: So your question is what are the expressions that generate xvalues. You should edit your question to make it more clear that you're looking for where xvalues come from, and also edit it to let people know that you've read that, but it specifically doesn't make that clear.Burgrave
@NicolBolas: Thanks for editing.. Sorry, I couldn't reply to you over the weekend..Gastronome
M
22

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.)

Mitchum answered 26/7, 2012 at 1:43 Comment(5)
Boo, just as I was building my list of examples and stumbled upon the quote. :( +1, should've done the tag edit after I finished my answer. :)Barbet
Thanks for the detailed list... this combined with other answers and lot of other googling somewhat cleared the confusions over the basic definition.Gastronome
Very useful answer, but one point still escapes my understanding. I understand that in Foo bar(){Foo foo; return foo;}, the expression foo is an xvalue in the return statement (please correct if wrong). I don't see it covered by the list you quoted.Pistachio
@Pistachio I think foo would be a prvalue. xvalue is always something which is of type rvalue reference and can be assigned to a rvalue reference. This is what my understanding so far is.Macaulay
I meant foo would be an lvalue. Thinking about it a bit more an xvalue is a rvalue which CAN BE assigned some identity. And only rvalues which can have identity are of type && basically when we cast them explicitly : std::move or returning from a function. So the expression foo is an lvalue but the function call bar() is an xvalue. You can assign bar() to a Foo&& and give it an identity.Macaulay
N
8

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.)

Nighttime answered 26/7, 2012 at 2:47 Comment(0)
L
4

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.

Lelea answered 2/3, 2021 at 12:26 Comment(0)
H
-2

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.

Hinton answered 26/7, 2012 at 2:53 Comment(7)
No, an xvalue is a value which is still valid, but about to be discarded. Which generally means it's safe to do things like "steal" resources from it and write over its existing value.Nighttime
@aschepler: So e.g. the return type of []() { int x = 5; return std::move(x); } isn't an xvalue? If not, then what is it?Hinton
Types do not have value categories; expressions do.Nighttime
@aschepler: Oh, okay, so how about ([]() { int x = 5; return std::move(x); })()?Hinton
A lambda-expression without trailing-return-type and containing more than one statement has implicit trailing-return-type void. If you move the declaration outside the lambda, the implicit trailing-return-type would be int, because the lvalue-to-rvalue conversion is applied when the return type must be deduced. If you make it []()-> int&& {...}, sure, that expression is an xvalue because it's a function call to the imaginary struct's operator(). But then there's no way to detect that it's a dangling reference.Nighttime
@aschepler: Yes, I forgot the return type. I indeed meant int&&. So you're telling me it's an x-value? Sure, but now you're just saying exactly the same thing I said in my answer ("an rvalue whose storage may have been deallocated")... why did you say that's wrong?Hinton
let us continue this discussion in chatNighttime

© 2022 - 2024 — McMap. All rights reserved.