[英]Usage of std::forward vs std::move
I always read that std::forward
is only for use with template parameters.我总是读到std::forward
仅用于模板参数。 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:现在,我想既然std::forward
应该返回一个左值引用,如果参数是一个左值引用,如果参数是一个 r 值引用,那么这段代码可以简化为这样的:
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).这有点类似于 cplusplus.com 提到的std::forward
示例(只是没有任何模板参数)。 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);
}
You cannot use std::forward
without explicitly specifying its template argument.如果不明确指定其模板参数,则不能使用std::forward
。 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."要理解这一点,您需要真正了解转发引用( T&&
为推导的T
)如何在内部工作,而不是将它们挥之不去,因为“这很神奇”。 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
:
foo(42);
42
is an rvalue of type int
. 42
是一个int
类型的右值。T
is deduced to int
. T
被推导为int
。bar
therefore uses int
as the template argument for std::forward
.因此,对bar
的调用使用int
作为std::forward
的模板参数。std::forward<U>
is U &&
(in this case, that's int &&
) so t
is forwarded as an rvalue. std::forward<U>
的返回类型为U &&
(在本例中为int &&
),因此t
作为右值转发。 Now, let's call foo
like this:现在,让我们像这样调用foo
:
int i = 42;
foo(i);
i
is an lvalue of type int
. i
是int
类型的左值。V
is used to deduce T
in a parameter of type T &&
, V &
is used for deduction.因为完美转发的特殊规则,当类型的左值的V
被用于推断T
在类型的参数T &&
, V &
用于扣除。 Therefore, in our case, T
is deduced to be int &
.因此,在我们的例子中, T
被推导出为int &
。 Therefore, we specify int &
as the template argument to std::forward
.因此,我们指定int &
作为std::forward
的模板参数。 Its return type will therefore be " int & &&
", which collapses to int &
.因此,它的返回类型将是“ int & &&
”,它会折叠为int &
。 That's an lvalue, so i
is forwarded as an lvalue.这是一个左值,所以i
被作为左值转发。
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<T>
, T
有时是引用(当原始是左值时),有时不是(当原始是右值时)。 std::forward
will therefore cast to an lvalue or rvalue reference as appropriate.因此, 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)
根本不接受左值这一事实——左值不能绑定到右值引用。
I recommend reading "Effective Modern C ++" by Scott Meyers , specifically:我建议阅读Scott Meyers 的“Effective Modern C++”,特别是:
std::move
and std::forward
.第 23 条:理解std::move
和std::forward
。From a purely technical perspective, the answer is yes:
std::forward
can do it all.从纯粹的技术角度来看,答案是肯定的:std::forward
可以做到这一切。std::move
isn't necessary.std::move
不是必需的。 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 .std::move
的吸引力在于方便、减少出错的可能性和更清晰。
This function accepts rvalues and 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.首先注意std::move
只需要一个函数参数,而std::forward
需要一个函数参数和一个模板类型参数。
This function accepts all and does perfect forwarding.该函数接受所有并进行完美转发。
template <typename T> void ImageView::setImage(T&& image){
_image = std::forward<T>(image);
}
You have to specify the template type in std::forward
.您必须在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
.在这种情况下, Image&& image
始终是一个右值引用,而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 值引用的函数不能接受左值,因此它不等同于前两个函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.