std :: move和std :: forward之间有什么区别


160

我在这里看到了这一点: Move构造函数调用基类Move构造函数

有人可以解释:

  1. 之间的差std::movestd::forward,优选用一些代码示例?
  2. 如何轻松思考,以及何时使用


“如何轻松地思考它,以及何时使用哪个”,move当您想移动一个值,以及forward想使用完美的转发时就使用它。这不是火箭科学;)
Nicol Bolas 2012年

move()执行无条件强制转换,而forward()则根据已传递的参数执行强制转换。
RaGa__M

Answers:


159

std::move接受一个对象,并允许您将其视为临时对象(右值)。尽管这不是语义要求,但是通常,接受对右值的引用的函数会使它无效。当您看到时std::move,表明该对象的值以后不应再使用,但是您仍然可以分配一个新值并继续使用它。

std::forward有一个用例:将模板化的函数参数(在函数内部)转换为用于传递它的调用方的值类别(左值或右值)。这允许将rvalue参数作为rvalue传递,并将lvalues作为lvalue传递,这种方案称为“完美转发”。

为了说明

void overloaded( int const &arg ) { std::cout << "by lvalue\n"; }
void overloaded( int && arg ) { std::cout << "by rvalue\n"; }

template< typename t >
/* "t &&" with "t" being template param is special, and  adjusts "t" to be
   (for example) "int &" or non-ref "int" so std::forward knows what to do. */
void forwarding( t && arg ) {
    std::cout << "via std::forward: ";
    overloaded( std::forward< t >( arg ) );
    std::cout << "via std::move: ";
    overloaded( std::move( arg ) ); // conceptually this would invalidate arg
    std::cout << "by simple passing: ";
    overloaded( arg );
}

int main() {
    std::cout << "initial caller passes rvalue:\n";
    forwarding( 5 );
    std::cout << "initial caller passes lvalue:\n";
    int x = 5;
    forwarding( x );
}

正如霍华德(Howard)所提到的,这两个函数也有相似之处,因为它们只是转换为引用类型。但是,除了这些特定的用例(覆盖了右值引用强制转换的99.9%的有用性)之外,您应该static_cast直接使用并为自己的工作写一个很好的解释。


我不知道它是准确的说std::forward唯一的用例是函数参数的完美转发。我遇到了想要完美转发其他事物(例如对象成员)的情况。
Geoff Romer

@GeoffRomer参数的成员?你有例子吗?
Potatoswatter 2013年

我张贴的例子作为一个单独的问题:stackoverflow.com/questions/20616958/...
杰夫卢氏

嗯,所以,如果我正确理解这一点,则意味着我可以编写一个函数forward(),其中forward(5)在5上进行操作,就像我按值传递一样,而forward(x)在x上进行操作,就像我通过值传递一样。引用而不必显式编写重载。
iheanyi 2014年

@iheanyi是的,就是这个主意!但这必须是模板,而不仅仅是普通功能。
Potatoswatter

63

两者std::forwardstd::move都不过是演员。

X x;
std::move(x);

上面的方法将x类型X 的左值表达式转换为类型X的右值表达式(确切地说是x值)。 move也可以接受右值:

std::move(make_X());

在这种情况下,它是一个标识函数:采用类型X的右值并返回类型X的右值。

随着std::forward您可以选择目标在一定程度上:

X x;
std::forward<Y>(x);

x类型X 的左值表达式强制转换为类型Y的表达式。Y的存在约束。

Y可以是X的可访问底数,也可以是X的底数的引用。Y可以是X或对X的引用。不能使用放弃cv限定词forward,但可以添加cv限定词。Y不能是只能从X转换的类型,除非通过可访问的Base转换。

如果Y是左值引用,则结果将是左值表达式。如果Y不是左值引用,则结果将是右值(精确地说是xvalue)表达式。

forward仅当Y不是左值引用时才可以使用右值参数。也就是说,您不能将右值转换为左值。出于安全原因,因为这样做通常会导致悬挂参考。但是将右值转换为右值是可以的并且可以的。

如果尝试将Y指定为不允许的值,则错误将在编译时而不是运行时捕获。


如果我使用完美地将对象转发到函数std::forward,那么在执行该函数之后,可以使用该对象吗?我知道,在的情况下std::move,这是未定义的行为。
iammilind

1
关于movestackoverflow.com/a/7028318/576911 对于forward,如果您传入一个左值,则您的API应该像接收左值一样作出反应。通常,这意味着该值将保持不变。但是,如果它是非常量左值,则您的API可能已对其进行了修改。如果您传入一个右值,通常意味着您的API可能已从其移走,因此适用stackoverflow.com/a/7028318/576911
Howard Hinnant

21

std::forward用于完全按照传递给函数的方式转发参数。就像这里显示的那样:

何时使用std :: forward转发参数?

使用std::move提供对象作为右值,以匹配移动构造函数或接受右值的函数。std::move(x)即使它x本身不是右值,它也会这样做。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.