我总是读到
std::forward
仅适用于模板参数。然而,我问自己为什么。请参阅以下示例:
void ImageView::setImage(const Image& image){
_image = image;
}
void ImageView::setImage(Image&& image){
_image = std::move(image);
}
这两个函数的作用基本相同;一个采用左值参考,另一个采用右值参考。现在,我想,如果参数是左值引用,则
std::forward
应该返回左值引用,如果参数是 1,则应该返回右值引用,因此可以将此代码简化为如下所示:
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);
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)
根本不接受左值——左值无法绑定到右值引用。
我建议阅读 Scott Meyers 所著的《Effective Modern C ++》,具体来说:
std::move
和 std::forward
。从纯粹的技术角度来看,答案是肯定的:
都可以。std::forward
没有必要。当然,这两个功能都没有 确实有必要,因为我们可以在任何地方编写强制转换,但我 希望我们都同意这会很糟糕。std::move
的景点 是方便、减少错误的可能性和更高的清晰度。std::move
此函数接受右值,但不能接受左值。
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);
}
您必须在
std::forward
中指定模板类型。
在这种情况下,
Image&& image
是always一个右值参考,std::forward<Image>
将始终移动,所以你不妨使用std::move
。
接受右值引用的函数不能接受左值,因此它不等于前两个函数。