使用std :: forward vs std :: move

时间:2015-03-03 09:24:58

标签: c++ c++11 move forward

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

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

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

这两个功能基本相同;一个采用l值参考,另一个采用r值参考。现在,我认为,如果参数是l值引用,std::forward应该返回l值引用,如果参数是1,则认为是r值引用,这个代码可以简化为这样:

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

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

我也问自己究竟与

有什么区别
void ImageView::setImage(Image& image){
    _image = std::forward(image);
}

3 个答案:

答案 0 :(得分:66)

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

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

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

假设我们这样称呼foo

foo(42);

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

现在,让我们这样打foo

int i = 42;
foo(i);

iint类型的左值。由于完美转发的特殊规则,当V类型的左值用于在T类型的参数中推导T &&时,V &用于扣除。因此,在我们的案例中,T被推断为int &

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

<强>摘要

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

您无法在非模板版本中正确使用此功能,因为您只能使用一种类型。更不用说setImage(Image&& image)根本不接受左值的事实 - 左值不能绑定到右值引用。

答案 1 :(得分:22)

我建议阅读&#34; Effective Modern C ++&#34; ,其作者是 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

}
  

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

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

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

答案 2 :(得分:7)

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

在此上下文中,Image&& image 总是是一个r值引用,std::forward<Image>将始终移动,因此您也可以使用std::move

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