is there any difference between static cast to rvalue reference and std::move
Asked Answered
A

4

32

The description for static cast says

If new_type is an rvalue reference type, static_cast converts the value of expression to xvalue. This type of static_cast is used to implement move semantics in std::move.(since C++11)

Does this confirm that the following are equivalent ?

(A)

X x1;
X x2 = static_cast<X&&>(x1); 

(B)

X x1;
X x2 = std::move(x1);
Aponte answered 17/6, 2013 at 7:14 Comment(1)
They are equivalent but move is less error prone.Valvular
Q
45

Yes there is a very important difference: std::move documents what you want to do. In addition the cast is prone to writing errors like a forgotten & or wrong type X.

As it can be seen, std::move is even less to type.

Quartan answered 17/6, 2013 at 7:19 Comment(1)
I use "static_cast<X&&>" only when move is not constexpr and I need that attribute.Smaze
S
4

In C++11, T&& is an rvalue reference. They behave like lvalue references from C++ 98/03. Their goal - to be a candidate for moving. In C++98 this construct can appear in reference collapsing.

std::move - turn expression into an rvalue. It could have been called rvalue_cast, but wasn't.

Explicit cast to type T&& is possible in principle. The official standard costs some money, but in the ISO/IEC 14882:2011 draft there's this:

5.2.9 Static cast

8)  

The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) conversions are applied to the operand....

From a practical point of view, it is more convenient to use std::move.

Consider this example:

#include <stdio.h>
#include <utility>

class A
{
public:
A () {printf ("A ()" "\n");}
A (const A &) {printf ("A (&)" "\n");}
A (A &&) {printf ("A (&&)" "\n");}
A (const A &&) {printf ("A (const &&)" "\n");}
~ A () {printf ("~ A ()" "\n");}
};


int main ()
{
const A obj;
A obj2 (std::move (obj)); // 1-st approach
A obj3 (static_cast <const A&&> (obj));  // 2-nd approach
}

For me, the first approach is:

  • more convenient (should you perform static_cast to const A&&, or to A&& ?)
  • more explicitly (I can use search in text editor to find std::move in the project)
  • less error-prone.
Steinberg answered 2/7, 2015 at 11:7 Comment(1)
no need for an rvalue_cast keyword. Just do template<typename T> template<typename T> constexpr auto rvalue_cast(T&& t) { return std::move(t); }; if you really want to use that name.Cockcroft
M
2

They are not strictly equivalent. std::move()'s implementation relies on static_cast:

template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

They differ in the sense that std::move() has remove_reference to deal with reference collapse. An example where (A) and (B) are not strictly equivalent:

// Set up different behavior for lvalue and rvalue.
class T {};
void func(T&& t) { std::cout << "rvalue captured.\n"; }
void func(T& t) { std::cout << "lvalue captured.\n"; }
// Example:
Using X = T&;
X x1;
X x2 = static_cast<X&&>(x1); // (A) "lvalue captured."
X x3 = std::move(x1);        // (B) "rvalue captured."
Mccahill answered 26/7, 2020 at 17:3 Comment(0)
B
-3

You can use static_cast<A &&>(a) when a is an rvalue, but you shouldn't use std::move(a).
When you use A && a = std::move(A()), you get a dangling reference.

The basic idea is that the lifetime of a temporary cannot be further extended by "passing it on": a second reference, initialized from the reference to which the temporary was bound, does not affect its lifetime.

std::move's implementation is somewhat like

template <typename T>
constexpr decltype(auto) move(T && __t) noexcept  // when used in std::move(A()),
                                                  // the lifetime of the temporary object is extended by __t
{
    return static_cast<typename std::remove_reference<T>::type &&>(__t);  // a xvalue returned, no lifetime extension
}

auto && a = std::move(A());  // the anonymous object wiil be destructed right after this line
Briefcase answered 28/9, 2017 at 12:47 Comment(6)
I don't see how you get dangling references in your second example. Also, what's wrong with std::move(a) when a is an rvalue? The result of std::move((const int &)a) is just const int &&, which is what you want.Anselma
@Anselma A temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference. move takes a rvalue reference to the to-be-casted prvalue expression as an argument.Briefcase
A&& a = std::move(A()); would be a dangling reference (as would the static_cast version of the same)... A a is not a reference, therefore not a dangling oneSumo
Your "corrected typo" now means you aren't answering the question; both std::move and static_cast<A&&> generate a dangling reference there., but the question is about whether those two things differ. Also you say in your comment that static_cast<A &&>(A()) is identical to a single A()", however std::move(A()) is also identical to the cast; and neither is exactly identical to A() , since they are xvalues and not prvalues (with associated lifetime extension problem that you already mentioned)Sumo
don't we have a new rule that extends temporaries life bound to rval references ? it was true for const ref already.Mckelvey
@Mckelvey just bound directly.Briefcase

© 2022 - 2025 — McMap. All rights reserved.