What's the Difference Between Std::Move and Std::Forward

What's the difference between std::move and std::forward

std::move takes an object and allows you to treat it as a temporary (an rvalue). Although it isn't a semantic requirement, typically a function accepting a reference to an rvalue will invalidate it. When you see std::move, it indicates that the value of the object should not be used afterwards, but you can still assign a new value and continue using it.

std::forward has a single use case: to cast a templated function parameter (inside the function) to the value category (lvalue or rvalue) the caller used to pass it. This allows rvalue arguments to be passed on as rvalues, and lvalues to be passed on as lvalues, a scheme called "perfect forwarding."

To illustrate:

void overloaded( int const &arg ) { std::cout << "by lvalue\n"; }
void overloaded( int && arg ) { std::cout << "by rvalue\n"; }

template< typename t >
/* "t &&" with "t" being template param is special, and adjusts "t" to be
(for example) "int &" or non-ref "int" so std::forward knows what to do. */
void forwarding( t && arg ) {
std::cout << "via std::forward: ";
overloaded( std::forward< t >( arg ) );
std::cout << "via std::move: ";
overloaded( std::move( arg ) ); // conceptually this would invalidate arg
std::cout << "by simple passing: ";
overloaded( arg );
}

int main() {
std::cout << "initial caller passes rvalue:\n";
forwarding( 5 );
std::cout << "initial caller passes lvalue:\n";
int x = 5;
forwarding( x );
}

As Howard mentions, there are also similarities as both these functions simply cast to reference type. But outside these specific use cases (which cover 99.9% of the usefulness of rvalue reference casts), you should use static_cast directly and write a good explanation of what you're doing.

std::move Vs std::forward

In this case:

void push_back(T&& value)
{
resizeIfRequired();
moveBackInternal(std::forward<T>(value)); // (1)
moveBackInternal(std::move(value)); // (2)

}

std::forward<T>(value) and std::move(value) are identical in this scenario (it doesn't matter between (1) and (2)... so use (2)).

move is an unconditional cast to xvalue. That line gives you an expression of type T&& that's an rvalue, always.

forward is a conditional cast. If T is an lvalue reference type, it yields an lvalue. Otherwise (if it's either not a reference type or an rvalue reference type), it yields an rvalue. In our case, T is not a reference type - so we get an rvalue.

Either way, we end up at the same point - we call moveBackInternal with value cast as an rvalue. Just move() is a simpler way of getting there. forward<T> works, but it's unnecessary.

Confusion between std::move and std::forward

In both cases you are constructing a string, std::string local, with an rvalue reference as argument. As a result, local is move-constructed from the original object referred-to by that reference.

This doesn't have much to do with std::forward or with std::move; furthermore, you are not initialising a reference (which is where "binding a reference to an lvalue" comes from in Meyers's text; the reference would be on the LHS) — you are simply constructing an object from another object.

However, it is true that without writing std::forward or std::move you would end up copying those function arguments instead, as the rvalue reference would be dropped on the initialisation side of local's declaration.

So, in this case, std::forward and std::move have the same effect. However, they are not the same thing and should generally not be treated as interchangeable. Read the following for more information:

  • What's the difference between std::move and std::forward

Trying to understand std::forward, std::move a little better

There are a lot of questions here that don't seem concisely answerable to me, but one stands out:

"What is the difference between std::move and std::forward?"

std::move is used to convert an lvalue reference to an rvalue reference, often to transfer the resources held by one lvalue to another.

std::forward is used to distinguish between lvalue and rvalue references, often in the case where a parameter of type rvalue reference is deduced to be an lvalue.

The upshot: If you want to branch based on what kind of reference an object was when passed, use std::forward. If you just want to pilfer the resources of an lvalue by means of converting to an rvalue, use std::move.

For more details, I found the following helpful: http://thbecker.net/articles/rvalue_references/section_01.html

What's difference between forward and move in the constructor of class?

If val_t is not an lvalue reference type, then value in constructor is an rvalue reference, and std::forward is equivalent to std::move.

If val_t is an lvalue reference, then so is value. std::forward then returns an lvalue reference and the code compiles. std::move returns an rvalue reference, which cannot be bound to m_value (which is an lvalue reference), and compiler reports an error.

Demo

The reason fact_t<std::string> f(str); fails to compile is it's trying to pass an lvalue to a parameter that expects an rvalue.

Usage of std::forward vs std::move

You cannot use std::forward without explicitly specifying its template argument. It is intentionally used in a non-deduced context.

To understand this, you need to really understand how forwarding references (T&& for a deduced T) work internally, and not wave them away as "it's magic." So let's look at that.

template <class T>
void foo(T &&t)
{
bar(std::forward<T>(t));
}

Let's say we call foo like this:

foo(42);
  • 42 is an rvalue of type int.
  • T is deduced to int.
  • The call to bar therefore uses int as the template argument for std::forward.
  • The return type of std::forward<U> is U && (in this case, that's int &&) so t is forwarded as an rvalue.

Now, let's call foo like this:

int i = 42;
foo(i);
  • i is an lvalue of type int.
  • Because of the special rule for perfect forwarding, when an lvalue of type V is used to deduce T in a parameter of type T &&, V & is used for deduction. Therefore, in our case, T is deduced to be int &.

Therefore, we specify int & as the template argument to std::forward. Its return type will therefore be "int & &&", which collapses to int &. That's an lvalue, so i is forwarded as an lvalue.

Summary

Why this works with templates is when you do std::forward<T>, T is sometimes a reference (when the original is an lvalue) and sometimes not (when the original is an rvalue). std::forward will therefore cast to an lvalue or rvalue reference as appropriate.

You cannot make this work in the non-template version precisely because you'll have only one type available. Not to mention the fact that setImage(Image&& image) would not accept lvalues at all—an lvalue cannot bind to rvalue references.

Why use std::forward rather than std::move for data member in rvalue reference constructor's initialization list?

In this example, both std::move and std::forward do the same thing.

This is different if you change the example to a deduced type, e.g.

template<typename Arg>
ClassRoom(Arg&& theStudents)
:students{std::forward<Arg>(theStudents)}
{
}

v.s.

template<typename Arg>
ClassRoom(Arg&& theStudents)
:students{std::move(theStudents)}
{
}

Then:

vector<string> local_students = /* ... */;
ClassRoom cr(local_students)

Arg deduces to vector<string>&, which means different things happen

Forward:

forward<vector<string>&> passes along the lvalue-reference, so the overload of vector::vector chosen is the copy constructor, local_students is unaffected

Move:

move<vector<string>&> casts the lvalue-reference to rvalue-reference, so the overload of vector::vector chosen is the move constructor, local_students is now in the moved-from state

Should I use std::move or std::forward in move ctors/assignment operators?

The question is: Are those really the move constructor / assignment operator for the class? Or do they only look like that from the corner of your eye?

struct X{
X(X&&); // move ctor #1

template<class T>
X(T&&); // perfect forwarding ctor #2

X& operator=(X&&); // move assignment operator #3

template<class T>
X& operator=(T&&); // perfect forwarding ass. operator #4
};

In a real move ctor (#1) and move assignment operator (#3), you will never use std::forward, since, as you correctly assessed, you will always move.

Note that std::forward never makes sense without a perfect forwarding template (T&&). That is exactly the case for #2 and #4. Here, you will never use std::move, since you don't know if you actually got an rvalue (A-OK) or an lvalue (not so much).

See this answer of mine for an explanation of how std::forward actually works.

Is it true that std::forward and std::move do not generate code?

Whether something "generates code" or not depends on the compiler and its settings. As the other answer shows, you can expect some extra code to be generated if the optimizations are disabled.

std::move and std::forward merely return a reference to the parameter, which doens't require any actions at runtime (the change in value category happens at compile-time), and if optimizations are enabled, any half-decent compiler will generate no code for them.

If you want no extra code to be generated even in debug builds, use a static_cast<T &&> instead of those functions.



Related Topics



Leave a reply



Submit