std::forward 与 std::move 的用法
Usage of std::forward vs std::move
我总是读到 std::forward
仅适用于模板参数。但是,我问自己为什么。请参阅以下示例:
void ImageView::setImage(const Image& image){
_image = image;
}
void ImageView::setImage(Image&& image){
_image = std::move(image);
}
这是两个基本相同的函数;一个采用左值引用,另一个采用右值引用。现在,我认为如果参数是左值引用,std::forward
应该是左值引用,如果参数是一个,则应该是右值引用,这段代码可以简化为像这样:
void ImageView::setImage(Image&& image){
_image = std::forward(image);
}
这有点类似于 cplusplus.com 提到的 std::forward
示例(只是没有任何模板参数)。我只是想知道,这是否正确,如果不正确,为什么。
我也在问自己
到底有什么不同
void ImageView::setImage(Image& image){
_image = std::forward(image);
}
您必须在 std::forward
中指定模板类型。
在此上下文中,Image&& image
始终 r 值参考,std::forward<Image>
将始终移动,因此您不妨使用 std::move
.
接受右值引用的函数不能接受左值,因此它不等同于前两个函数。
您不能使用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>
的 return 类型是 U &&
(在本例中是 int &&
),因此 t
被作为右值转发。
现在,我们这样调用 foo
:
int i = 42;
foo(i);
i
是 int
. 类型的左值
- 由于完美转发的特殊规则,当
V
类型的左值用于推导T &&
类型参数中的T
时,V &
是用于扣除。因此,在我们的例子中,T
被推断为 int &
.
因此,我们指定 int &
作为 std::forward
的模板参数。因此,它的 return 类型将是“int & &&
”,它会折叠成 int &
。这是一个左值,所以 i
被作为左值转发。
总结
为什么这适用于模板是因为当您执行 std::forward<T>
时,T
有时是引用(当原始值是左值时)有时不是(当原始值是右值时)。 std::forward
因此将根据需要转换为左值或右值引用。
您不能准确地在非模板版本中进行这项工作,因为您只有一种类型可用。更不用说 setImage(Image&& image)
根本不接受左值这一事实——左值不能绑定到右值引用。
我推荐阅读 Scott Meyers 的“Effective Modern C++”,特别是:
- 项目 23:理解
std::move
和 std::forward
。
- 项目24:区分通用引用为右值引用。
From a purely technical perspective, the answer is yes: std::forward
can do it all. std::move
isn’t necessary. Of course, neither function
is really necessary, because we could write casts everywhere, but I
hope we agree that that would be, well, yucky. std::move
’s attractions
are convenience, reduced likelihood of error, and greater clarity.
右值参考
此函数接受右值,不能接受左值。
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
仅适用于模板参数。但是,我问自己为什么。请参阅以下示例:
void ImageView::setImage(const Image& image){
_image = image;
}
void ImageView::setImage(Image&& image){
_image = std::move(image);
}
这是两个基本相同的函数;一个采用左值引用,另一个采用右值引用。现在,我认为如果参数是左值引用,std::forward
应该是左值引用,如果参数是一个,则应该是右值引用,这段代码可以简化为像这样:
void ImageView::setImage(Image&& image){
_image = std::forward(image);
}
这有点类似于 cplusplus.com 提到的 std::forward
示例(只是没有任何模板参数)。我只是想知道,这是否正确,如果不正确,为什么。
我也在问自己
到底有什么不同void ImageView::setImage(Image& image){
_image = std::forward(image);
}
您必须在 std::forward
中指定模板类型。
在此上下文中,Image&& image
始终 r 值参考,std::forward<Image>
将始终移动,因此您不妨使用 std::move
.
接受右值引用的函数不能接受左值,因此它不等同于前两个函数。
您不能使用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>
的 return 类型是U &&
(在本例中是int &&
),因此t
被作为右值转发。
现在,我们这样调用 foo
:
int i = 42;
foo(i);
i
是int
. 类型的左值
- 由于完美转发的特殊规则,当
V
类型的左值用于推导T &&
类型参数中的T
时,V &
是用于扣除。因此,在我们的例子中,T
被推断为int &
.
因此,我们指定 int &
作为 std::forward
的模板参数。因此,它的 return 类型将是“int & &&
”,它会折叠成 int &
。这是一个左值,所以 i
被作为左值转发。
总结
为什么这适用于模板是因为当您执行 std::forward<T>
时,T
有时是引用(当原始值是左值时)有时不是(当原始值是右值时)。 std::forward
因此将根据需要转换为左值或右值引用。
您不能准确地在非模板版本中进行这项工作,因为您只有一种类型可用。更不用说 setImage(Image&& image)
根本不接受左值这一事实——左值不能绑定到右值引用。
我推荐阅读 Scott Meyers 的“Effective Modern C++”,特别是:
- 项目 23:理解
std::move
和std::forward
。 - 项目24:区分通用引用为右值引用。
From a purely technical perspective, the answer is yes:
std::forward
can do it all.std::move
isn’t necessary. Of course, neither function is really necessary, because we could write casts everywhere, but I hope we agree that that would be, well, yucky.std::move
’s attractions are convenience, reduced likelihood of error, and greater clarity.
右值参考
此函数接受右值,不能接受左值。
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);
}