使用std :: forward和std :: move

我总是读std::forward只能用于模板参数。 但是,我在问自己为什么。 看下面的例子:

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

这两个function基本上是一样的; 一个采取一个l值参考,另一个r值参考。 现在,我认为自从std::forward应该返回一个l值引用,如果参数是一个l值引用和一个r值引用,如果参数是一个,这个代码可以简化为这样的:

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

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

我也在问自己究竟会有什么不同

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

不能使用std::forward而不明确指定它的模板参数。 它被有意使用在一个非推断的上下文中。

为了理解这一点,你需要真正理解转发引用( T&&对于推导出的T )是如何在内部工作的,而不是把它们当作“魔法”。 所以让我们来看看。

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

比方说,我们这样称呼foo

 foo(42); 

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

现在,让我们调用foo像这样:

 int i = 42; foo(i); 

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

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

概要

为什么这与模板一起工作是当你做std::forward<T>T有时是一个引用(当原始文件是一个左值),有时不是(当原文是右值时)。 因此, std::forward将根据需要转换为左值或右值引用。

您无法精确地在非模板版本中进行这项工作,因为您只能使用一种types。 更不用说setImage(Image&& image)根本就不接受左值的事实 – 左值不能绑定右值引用。

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

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

项目24:区分右值引用的通用引用。

从纯粹的技术angular度来看,答案是肯定的: std :: forward可以做到这一切。 std :: move是没有必要的。 当然,这两个function都不是真的有必要,因为我们可以在任何地方写剧本,但是我希望我们也同意这样做。 标准::移动的景点是方便,减less错误的可能性,更清晰

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需要函数参数和模板types参数。

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

通用参考(转发参考) :该function接受所有参数,并完成转发。

您必须在std::forward指定模板types。

在这种情况下, Image&& image 总是一个r值引用, std::forward<Image>将总是移动,所以你不妨使用std::move

你的函数接受一个r值引用不能接受l值,所以它不等于前两个函数。