std::forward 与 std::move 的用法[重复]

问题描述 投票:0回答:3

我总是读到

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);
}
c++ c++11 perfect-forwarding stdmove
3个回答
131
投票

如果没有显式指定其模板参数,您不能使用

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)
根本不接受左值——左值无法绑定到右值引用。


41
投票

我建议阅读 Scott Meyers 所著的《Effective Modern C ++》,具体来说:

  • 第 23 项:了解
    std::move
    std::forward
  • 第 24 条:区分右值引用的通用引用。

从纯粹的技术角度来看,答案是肯定的:

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);
}

8
投票

您必须在

std::forward
中指定模板类型。

在这种情况下,

Image&& image
always一个右值参考,
std::forward<Image>
将始终移动,所以你不妨使用
std::move

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

© www.soinside.com 2019 - 2024. All rights reserved.