Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Usage of std::forward vs std::move

I always read that std::forward is only for use with template parameters. However, I was asking myself why. See the following example:

void ImageView::setImage(const Image& image){     _image = image; }  void ImageView::setImage(Image&& image){     _image = std::move(image); } 

Those are two functions which basically do the same; one takes an l-value reference, the other an r-value reference. Now, I thought since std::forward is supposed to return an l-value reference if the argument is an l-value reference and an r-value reference if the argument is one, this code could be simplified to something like this:

void ImageView::setImage(Image&& image){     _image = std::forward(image); } 

Which is kind of similar to the example cplusplus.com mentions for std::forward (just without any template parameters). I'd just like to know, if this is correct or not, and if not why.

I was also asking myself what exactly would be the difference to

void ImageView::setImage(Image& image){     _image = std::forward(image); } 
like image 702
bweber Avatar asked Mar 03 '15 09:03

bweber


People also ask

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

std::move takes an object and casts it as an rvalue reference, which indicates that resources can be "stolen" from this object. std::forward has a single use-case: to cast a templated function parameter of type forwarding reference ( T&& ) to the value category ( lvalue or rvalue ) the caller used to pass it.

What is the use of std :: forward?

The idiomatic use of std::forward is inside a templated function with an argument declared as a forwarding reference , where the argument is now lvalue , used to retrieve the original value category, that it was called with, and pass it on further down the call chain (perfect forwarding).

Where is std :: move defined?

In C++11, std::move is a standard library function that casts (using static_cast) its argument into an r-value reference, so that move semantics can be invoked. Thus, we can use std::move to cast an l-value into a type that will prefer being moved over being copied. std::move is defined in the utility header.

What is perfect forwarding C++?

What is Perfect Forwarding. Perfect forwarding allows a template function that accepts a set of arguments to forward these arguments to another function whilst retaining the lvalue or rvalue nature of the original function arguments.


1 Answers

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.

like image 101
Angew is no longer proud of SO Avatar answered Sep 21 '22 11:09

Angew is no longer proud of SO