我一直认为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);
}
答案 0 :(得分:66)
如果没有明确指定其模板参数,就无法使用std::forward
。它故意用于非推断的上下文中。
要理解这一点,您需要真正理解转发引用(T&&
对于推导的T
)如何在内部工作,而不是将它们视为“它的魔力”。那么让我们来看看。
template <class T>
void foo(T &&t)
{
bar(std::forward<T>(t));
}
假设我们这样称呼foo
:
foo(42);
42
是int
类型的右值。 T
被推断为int
。因此,对bar
的调用使用int
作为std::forward
的模板参数。 std::forward<U>
的返回类型为U &&
。在这种情况下,那是int &&
,因此t
将作为右值转发。
现在,让我们这样打foo
:
int i = 42;
foo(i);
i
是int
类型的左值。由于完美转发的特殊规则,当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值,因此它不等同于前两个函数。