使用std :: forward vs std :: move

bwe*_*ber 53 c++ move forward c++11

我一直认为std::forward这只适用于模板参数.但是,我问自己为什么.请参阅以下示例:

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

void ImageView::setImage(Image&& image){
    _image = std::move(image);
}
Run Code Online (Sandbox Code Playgroud)

这两个功能基本上是一样的; 一个采用l值参考,另一个采用r值参考.现在,我认为,std::forward如果参数是l值引用,则应返回l值引用,如果参数为1,则应返回r值引用,此代码可简化为如下所示:

void ImageView::setImage(Image&& image){
    _image = std::forward(image);
}
Run Code Online (Sandbox Code Playgroud)

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

我也问自己究竟会有什么区别

void ImageView::setImage(Image& image){
    _image = std::forward(image);
}
Run Code Online (Sandbox Code Playgroud)

Rei*_*ica 74

如果没有明确指定其模板参数,则无法使用std::forward.它故意用于非推断的上下文中.

要理解这一点,你需要真正理解如何在内部转发引用(T&&对于推导的T)工作,而不是将它们视为"它的魔力".那么让我们来看看.

template <class T>
void foo(T &&t)
{
  bar(std::forward<T>(t));
}
Run Code Online (Sandbox Code Playgroud)

假设我们这样称呼foo:

foo(42);
Run Code Online (Sandbox Code Playgroud)

42是一个类型的右值int.T被推断为int.bar因此,调用int用作模板参数std::forward.返回类型std::forward<U>U &&.在这种情况下,这是int &&,因此t作为右值转发.

现在,让我们这样打电话foo:

int i = 42;
foo(i);
Run Code Online (Sandbox Code Playgroud)

i是一个左右的类型int.由于完美转发的特殊规则,当使用类型的左值V来推导T类型参数时T &&,V &用于推导.因此,在我们的例子中,T被推断为int &.

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

摘要

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

您无法在非模板版本中精确地工作,因为您只有一种类型可用.更不用说setImage(Image&& image)不接受左值的事实- 左值不能绑定到右值引用.

  • @ user1488118有参考折叠规则.`T && &&`折叠为`T &&`,所有其他(`T &&&`,`T &&&`和`T &&`)崩溃为`T&'.---是的,推导出'V&'是允许你用左值和右值(不一定是引用)来调用函数模板的.采用`Image &&`的非模板函数只能用`Image`类型的rvalues调用. (4认同)
  • 好的,我想到目前为止我已经明白了。我想知道的是:我可以使用左值引用和右值引用调用函数`template &lt;typename T&gt; void foo(T&amp;&amp; t)`?如果它是一个左值引用,T 被推导出为 `V&amp;`,这意味着参数类型变成了 `(V&amp; &amp;&amp;)`,这基本上是一个对左值引用(或什么?)的 rvlaue 引用?所以这是行为与非模板函数不同的地方,其中函数 `void foo(MyType&amp;&amp; t)` 只能用 r 值引用调用? (2认同)
  • `foo(i)`将`T`推导为`int`.`FOO(42)`[不会编译](http://coliru.stacked-crooked.com/a/46e3f0077c495103),因为42是一个右值,并且不能结合到一个非const左值参考.使用"V&"而不是"V"的特殊规则仅适用于模板参数"T"的函数参数类型为"T &&"的情况.任何其他原因导致规则不适用. (2认同)

Ron*_*ang 22

我推荐阅读"Effective Modern C++",其作者是Scott Meyers.

第23项:了解std :: move和std :: forward.

第24项:区分右值参考的通用引用.

从纯粹的技术角度来看,答案是肯定的:std :: forward可以做到这一切.std :: move不是必需的.当然,这两种功能都不是必需的,因为我们可以在任何地方写出演员,但我希望我们同意那将是,令人讨厌的.std :: move的吸引力很方便,错误的可能性降低,清晰度更高.

rvalue-reference:此函数接受rvalues不能接受左值.

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

}
Run Code Online (Sandbox Code Playgroud)

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

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

通用引用(转发引用):此功能接受所有并完成转发.

  • NB"通用参考"的正式名称现在是_转发参考_ (3认同)
  • @JohannGerell Herb Sutter也在CppCon 2014上谈到了它,最新版的Effective C++也提到了它,IIRR. (3认同)
  • 谢谢,作为参考,_forwarding reference_在[N4296]的§14.8.2.3中定义(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf) (3认同)

Chr*_*rew 7

您必须在中指定模板类型std::forward.

在这种情况下,Image&& image总是一个r值引用,并且std::forward<Image>总是会移动,所以你也可以使用它std::move.

接受r值引用的函数不能接受l值,因此它不等于前两个函数.