Usage of std::forward vs std::move

情到浓时终转凉″ 提交于 2019-11-27 00:05:57

问题


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);
}

回答1:


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.




回答2:


I recommend reading "Effective Modern C ++" ,its author is Scott Meyers.

Item 23: Understand std :: move and std :: forward.

Item 24: Distinguish universal references for rvalue references.

From a purely technical perspective, the answer is yes: std::forward can do it all. std::move isn’t necessary. Of course, neither function is really necessary, because we could write casts everywhere, but I hope we agree that that would be,well, yucky. std::move’s attractions are convenience, reduced likelihood of error, and greater clarity.

rvalue-reference:This function accepts rvalues cannot accept lvalues.

void ImageView::setImage(Image&& image){
    _image = std::forward(image); //error 
    _image = std::move(image);//conventional
    _image = std::forward<Image>(image);//unconventional

}

Note first that std::move requires only a function argument , while std::forward requires both a function argument and a template type argument.

template <typename T> void ImageView::setImage(T&& image){
    _image = std::forward<T>(image);
}

universal references(forwarding references):This function accepts all and does perfect forwarding.




回答3:


You have to specify the template type in std::forward.

In this context Image&& image is always an r-value reference and std::forward<Image> will always move so you might as well use std::move.

Your function accepting an r-value reference cannot accept l-values so it is not equivalent to the first two functions.



来源:https://stackoverflow.com/questions/28828159/usage-of-stdforward-vs-stdmove

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!