C++ std::forward 与 std::move 的使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28828159/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 21:04:21  来源:igfitidea点击:

Usage of std::forward vs std::move

c++c++11moveforward

提问by bweber

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

我总是读到它std::forward仅用于模板参数。然而,我问自己为什么。请参阅以下示例:

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::forwardis 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:

这是两个基本相同的功能;一个采用左值参考,另一个采用右值参考。现在,我想既然std::forward应该返回一个左值引用,如果参数是左值引用,如果参数是一个右值引用,那么这段代码可以简化为这样的:

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.

这有点类似于 cplusplus.com 提到的示例std::forward(只是没有任何模板参数)。我只想知道,这是否正确,如果不正确,为什么。

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

我也在问自己到底有什么区别

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

回答by Angew is no longer proud of SO

You cannotuse std::forwardwithout explicitly specifying its template argument. It is intentionally used in a non-deduced context.

如果不明确指定其模板参数,则不能使用std::forward。它有意用于非推导的上下文中。

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.

要理解这一点,您需要真正了解转发引用(T&&对于推导T)如何在内部工作,而不是将它们挥之不去,因为“它很神奇”。让我们来看看。

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

Let's say we call foolike this:

假设我们这样调用foo

foo(42);

42is an rvalue of type int. Tis deduced to int. The call to bartherefore uses intas the template argument for std::forward. The return type of std::forward<U>is U &&. In this case, that's int &&, so tis forwarded as an rvalue.

42是一个类型的右值intT推导出来intbar因此int,对的调用用作 的模板参数std::forward。的返回类型std::forward<U>U &&。在这种情况下,就是int &&,所以t作为右值转发。

Now, let's call foolike this:

现在,让我们这样调用foo

int i = 42;
foo(i);

iis an lvalue of type int. Because of the special rule for perfect forwarding, when an lvalue of type Vis used to deduce Tin a parameter of type T &&, V &is used for deduction. Therefore, in our case, Tis deduced to be int &.

i是一个类型的左值int。由于完美转发的特殊规则,当类型的左值V用于推导T类型的参数时T &&V &使用推导。因此,在我们的例子中,T被推导出为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 iis forwarded as an lvalue.

因此,我们将 指定int &为模板参数std::forward。因此,它的返回类型将是“ int & &&”,它会折叠为int &. 这是一个左值,因此i作为左值转发。

Summary

概括

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

为什么这适用于模板是当你这样做时std::forward<T>T有时是参考(当原始是左值时),有时不是(当原始是右值时)。std::forward因此将根据需要转换为左值或右值引用。

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.

您无法在非模板版本中准确地进行此操作,因为您将只有一种类型可用。更不用说setImage(Image&& image)根本不接受左值的事实——左值不能绑定到右值引用。

回答by Ron Tang

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

我推荐阅读“Effective Modern C++”,它的作者是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.

第 23 条:理解 std::move 和 std::forward。

第 24 条:区分右值引用的通用引用。

从纯粹的技术角度来看,答案是肯定的:std::forward 可以做到这一切。std::move 不是必需的。当然,这两个函数都不是必需的,因为我们可以在任何地方编写强制转换,但我希望我们同意这会很糟糕。std::move 的吸引力在于方便、减少出错的可能性和更清晰

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

rvalue-reference:这个函数接受右值不能接受左值。

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.

首先注意 std::move 只需要一个函数参数,而 std::forward 需要一个函数参数和一个模板类型参数。

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.

通用引用(转发引用):此函数接受所有并进行完美转发。

回答by Chris Drew

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

您必须在std::forward.

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

在此上下文Image&& image始终是 r 值引用并且std::forward<Image>将始终移动,因此您不妨使用std::move.

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

您接受 r 值引用的函数不能接受左值,因此它不等同于前两个函数。