简体   繁体   English

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

[英]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
  • The call to bar therefore uses int as the template argument for std::forward .因此,对bar的调用使用int作为std::forward的模板参数。
  • The return type of 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 . iint类型的左值。
  • 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.因为完美转发的特殊规则,当类型的左值的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++”,特别是:

  • Item 23 : Understand std::move and std::forward .第 23 条:理解std::movestd::forward
  • Item 24 : Distinguish universal references for rvalue references.第 24 条:区分右值引用的通用引用。

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的吸引力在于方便、减少出错的可能性和更清晰

rvalue-reference右值引用

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需要一个函数参数和一个模板类型参数。

Universal references (forwarding references)通用引用(转发引用)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM