Why can std::move() force move semantics and I can't?

std::move() casts any given parameter into a rvalue reference

Suppose I have a struct obj

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct obj
{
	obj()
	{}

	obj(obj&& rv)
	{}

	obj& operator=(obj&& other)
	{
           cout << "move assignment forced!";

	   return *this;
	}
};

obj f()
{
   return obj{};
}


and I want to force the move assignment operator to be called

1
2
3
4
5
6
7
8
9
10
11
obj o1;

obj&& ref = f();

o1 = f();         // OK
o1 = move(ref);   // OK
o1 = move(f());   // OK
o1 = static_cast<obj&&>(f());  // the same cast that std::move() uses - OK
o1 = static_cast<obj&&>(ref);  // the same cast that std::move() uses - OK

o1 = ref;   // error: cannot bind lvalue to rvalue reference 


I don't understand why the last assignment is illegal.
Yes, of course ref is itself a lvalue, and a lvalue cannot be bound to a rvalue reference.

BUT the second-last line makes me laugh:

I'm casting ref (which is a rvalue reference) to a rvalue reference...

wtf? it's like pouring water into water

Both ref and static_cast<obj&&>(ref) are rvalue references, with the only difference that the second will trigger the Move Assignment... the first won't

The only reasonable explanation here is that static_cast<T&&>() doesn't cast to a rvalue reference but to a temporary... there's no other explanation in my mind.

Thanks in advance!
Last edited on
Both ref and static_cast<obj&&>(ref) are rvalue references
Correct, but that doesn't matter; the compiler doesn't automatically move from lvalues.
ref is a lvalue;
static_cast<obj&&>(ref) is a xvalue (a kind of rvalue).

Your confusion is probably the (usually understated) difference between value category and type. Still this simplified mental model applies: "everything with a name is an lvalue." Remember that named variables of rvalue reference type are just named aliases (i.e., lvalues) bound to an rvalue.
Last edited on
std::move() returns, by definition, an rvalue reference

What's the difference between the rvalue reference returned by move() and mine?
In the declaration obj&& ref = f(); the identifier ref names a variable of type 'rvalue reference to obj'

In static_cast<obj&&>(ref) or o1 = ref, the expression (id-expression) ref has type 'obj' and value category 'lvalue'. (The type of an expression is always some non-reference type.)

In other words, even if the type of a variable is 'rvalue reference to X', the id-expression consisting of the name of the variable yields an lvalue of (non-reference) type X.
Thanks for the answer.

I'm very sorry but i'm still a bit confused.

You said that


In static_cast<obj&&>(ref) OR o1 = ref
ref has type 'obj' and value category 'lvalue'


Yes. Ref, since it has a name and an address, is indeed an lvalue.

When people talk about std::move() they say: move returns a temporary... but that's not true as far as I know!

I looked at move's implementation, and that's just a static_cast which casts whatever the original type is, into an rvalue reference and returns it.

So if I'm calling move() with an lvalue int a = 10; move(a) move() is supposed to return an rvalue reference to a.

It's wrong to say that a magically becomes a temporary.

If what I've said is correct, then I at least understood how move() works.

With that said...

o1 = ref

The code doesn't compile because the Move Assignment is expecting a temporary that the rvalue reference can bind to.

Ref is not a temporary, but a lvalue. So alright.

o1 = static_cast<obj&&>(ref);

This compiles... and doesn't make sense to me.

o1 = move(ref);

We said earlier that move() takes any type and makes it an rvalue reference.

This, to me, is as confusing as the last example.

We're casting an rvalue reference to an rvalue reference.

This time, since move() is a function, we also return it.

---

Let's put it this way:

The Move Assignment wants a temporary.

o1 = ref can't give it because ref is not a temporary

o1 = static_cast<obj&&>(ref) why does it return a temporary?

o1 = move(ref) why does it return a temporary? move() is supposed to return a REFERENCE (obj&&) and NOT A TEMPORARY




In other words, even if the type of a variable is 'rvalue reference to X', the id-expression consisting of the name of the variable yields an lvalue of (non-reference) type X.


Has this rule an official documentation page?
Last edited on
> We're casting an rvalue reference to an rvalue reference.

We are casting an lvalue of type obj (the id-expression ref) to an rvalue (an xvalue) of type obj.
(Note that the result of the cast is un-named)


> Has this rule an official documentation page?

8/5 If an expression initially has the type “reference to T”, the type is adjusted to T prior to any further analysis. ...

http://eel.is/c++draft/expr

8.1.4.1/1 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 expression is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise; ...

http://eel.is/c++draft/expr.prim

Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int&& abc = 7 ;
// the expression (abc) is an lvalue of type int
// (the named entity is a variable)

// the expression static_cast<int&&>(abc) is an xvalue of type int

// int&& def = abc ; // *** error: abc is an lvalue of type int
                     //            we can't bind an rvalue reference to it

int&& def = static_cast<int&&>(abc) ; // fine: static_cast<int&&>(abc) is an rvalue
                                      //       we can bind an rvalue reference to it

enum colour { RED, GREEN, BLUE } ;
// the expression (GREEN) is a prvalue of type colour
// (the named entity is not a function, variable, or data member)

colour&& ghi = GREEN ;
// the expression (ghi) is an lvalue of type colour
// (the named entity is a variable) 



We are casting an lvalue of type obj (the id-expression ref) to an rvalue (an xvalue) of type obj.
(Note that the result of the cast is un-named)


Does the static_cast ALWAYS return an xvalue (rvalue)?

From http://en.cppreference.com/w/cpp/language/value_category


The following expressions are xvalue expressions:

[...]
* a cast expression to rvalue reference to object type, such as static_cast<char&&>(x);


So in my case static_cast<obj&&>(ref) returns an xvalue.

Does the same apply for static_cast<int&>(x) or static_cast<int>(x)?


Does the static_cast ALWAYS return an xvalue (rvalue)?

-----------------

std::move() uses static_cast<type&&>() to generate an rvalue out of an lvalue.

Since the return type of move() is type&&, the rvalue (generated by the cast) in then returned as an rvalue reference that holds it

And here's the second question: the function call move() returns an rvalue or the rvalue reference to the rvalue generated by the cast?

My confusion in this case comes from the fact that

1
2
3
int f();      // f returns an rvalue -> we can't assign to f()
int& f()     // f returns an lvalue -> we can assign to f() 
int&& f()   // what does f return? -> can we assign to f()? 
Last edited on
> Does the static_cast ALWAYS return an xvalue (rvalue)?

No. Assuming that T is some object-type, and the cast is valid:

static_cast<T>(expr) yields a prvalue of type T

static_cast<T&>(expr) yields an lvalue of type T

static_cast<T&&>(expr) yields an xvalue of type T


> the function call move() returns an rvalue or the rvalue reference to the rvalue generated by the cast?
> int&& f() // what does f return?

If x is an expression of some object-type T, the expression std::move(x) yields an xvalue of type T

The expression f() yields an xvalue of type int.


> can we assign to f()

No. int is a scalar type, its assignment operator is the built-in assignment operator; and the left hand side of a built-in assignment must be an lvalue.
Does the same apply for static_cast<int&>(x) or static_cast<int>(x)?>

there's a cppreference page for static_cast too, http://en.cppreference.com/w/cpp/language/static_cast

to quote,

As with all cast expressions, the result is:
an lvalue if new_type is an lvalue reference type or an rvalue reference to function type;
an xvalue if new_type is an rvalue reference to object type;
a prvalue otherwise.
Thank you for the explanation.

The root of my confusion is:

static_cast<obj&&>(ref)

I read this cast as: Take ref, whatever type it is and make it an rvalue reference (in the context of the expression obviously... c++ is a strongly typed language, you can't change the type of a variable)

What you are telling me, instead, is that the static_cast does not make my ref an rvalue reference... as the circumstance may confuse... but instead makes a completely new rvalue out of it.

Am I correct?

1
2
3
float var = 5;

int&& rv = static_cast<int>(var);


Before today, I thought this code would have never run!

Another one...

1
2
3
int a = 5;

static_cast<int&>(a) = 10;


Crazy! I never thought static_cast could actually yield lvalues...
> Am I correct?

Yes. This may help clarify the situation. Consider:
1
2
3
4
int i = 7 ;

auto d = static_cast<double>(i) ;
// i continues to be an int, but the result of the cast is an (unnamed) prvalue of type double. 



i continues to be an int, but the result of the cast is an (unnamed) prvalue of type double


Why does d evaluate as double and not double&& ?

Since the result of the cast is an rvalue...

Is this an exception just because it is a prvalue?
Last edited on
If auto is used to declare a variable, the rules for deducing its type is the same as the rules for template argument deduction from a function call. (Top-level references and cv-qualifiers are discarded).
Thank you all very much :)
Topic archived. No new replies allowed.