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

aCu*_*ria 155 c++ perfect-forwarding c++11

我在这里看到了这一点: Move Constructor调用基类Move Constructor

有人能解释一下:

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

Pot*_*ter 148

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

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

为了举例说明:

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 );
}
Run Code Online (Sandbox Code Playgroud)

正如霍华德所提到的那样,这些函数只是简单地转换为引用类型,也有相似之处.但是在这些特定用例之外(它覆盖了右值参考模型的99.9%的有用性),你应该static_cast直接使用并写下你正在做的事情的一个很好的解释.

  • @iheanyi 是的,就是这个主意!但它必须是一个模板,而不仅仅是一个普通的函数。 (2认同)

How*_*ant 61

无论std::forwardstd::move什么都不是,但转换.

X x;
std::move(x);
Run Code Online (Sandbox Code Playgroud)

上面将x类型X 的左值表达式转换为类型X的rvalue表达式(准确地说是xvalue). move也可以接受右值:

std::move(make_X());
Run Code Online (Sandbox Code Playgroud)

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

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

X x;
std::forward<Y>(x);
Run Code Online (Sandbox Code Playgroud)

x类型X 的左值表达式转换为类型为Y的表达式.对Y的约束有约束.

Y可以是X的可访问基数,也可以是X的基数的引用.Y可以是X,也可以是对X的引用.一个人不能抛弃cv-qualifiers forward,但可以添加cv-qualifiers.除了通过可访问的Base转换之外,Y不能是仅可从X转换的类型.

如果Y是左值引用,则结果将是左值表达式.如果Y不是左值引用,则结果将是rvalue(精确的xvalue)表达式.

forward只有当Y不是左值引用时,才能获取rvalue参数.也就是说,你不能将左值转换为左值.这是出于安全原因,因为这样做通常会导致悬挂参考.但是将rvalue转换为右值是可以的并且允许.

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


Bo *_*son 21

std::forward用于转发参数的方式与传递给函数的方式完全相同.就像这里显示的那样:

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

使用std::move提供对象作为右值,可能匹配移动构造函数或接受rvalues的函数.std::move(x)即使它x本身不是右值也是如此.


Enr*_*lis 10

我认为比较两个示例实现可以提供很多关于它们的用途和不同之处的见解。

我将从std::move,我在理解之前很久就真正理解了std::forward

std::move

长话短说:std::move用于将任何东西变成右值,目的是让它看起来像一个临时的(即使它不是:)std::move(non_temporary),这样它的资源就可以从中窃取(即从它移走)。

std::move(x)嗨,大家好,要知道谁我给这x对可以使用和他喜欢仔细分解,所以您通常使用它在右值引用参数,因为你一定就必然要临时工。

这是一个 C++14 实现,std::move与 Scott Meyers 在Effective Modern C++ 中展示的非常相似(在书中,返回类型std::remove_reference_t<T>&&更改为decltype(auto),这是从return语句中推导出来的)

template<typename T>
std::remove_reference_t<T>&& move(T&& t) {
    return static_cast<std::remove_reference_t<T>&&>(t);
}
Run Code Online (Sandbox Code Playgroud)

由此我们可以观察到以下几点std::move

  • 它是一个模板函数,所以它适用于任何类型T
  • 它通过通用(或转发)引用T&&获取其唯一参数,因此它可以对左值和右值进行操作;T将相应地推导出为左值引用或非引用类型;
  • 模板类型推演在地方,所以你不必通过指定的模板参数<…>,并在实践中,你不应该指定它;
  • 这也意味着std::move无非是static_cast基于非模板参数自动确定的模板参数,其类型是推导的;
  • 通过使用右值引用类型(而不是非引用类型)作为返回类型,它返回一个右值而不进行任何复制;它通过从T、via 中剥离任何引用std::remove_reference_t,然后添加&&.

琐事

你知道吗,旁边的std::move<utility>我们所谈论的,还有另外一个?是的,它std::move来自 from<algorithm>,它做了一件几乎不相关的事情:它是一个版本,std::copy它不是将值从一个容器复制到另一个容器,而是使用from移动它们;所以它是一个使用另一个.std::move<utility>std::movestd::move

std::forward

长话短说:std::forward用于将参数从函数内部转发到另一个函数,同时告诉后者函数是否使用临时函数调用前者。

std::forward<X>(x) 说两件事之一:

  • (如果x绑定到一个右值,即临时的)函数先生你好,我从另一个函数那里收到了这个包裹,在你使用它后不需要它,所以请随意使用它
  • (如果x绑定到左值,即非临时)函数先生您好,我已经从另一个函数那里收到了这个包裹,他在您使用它后确实需要它,所以请不要破坏它

所以你通常在转发/通用引用上使用它,因为它们可以绑定到临时和非临时。

换句话说,std::forward是为了能够把这个代码

template<typename T>
void wrapper(T&& /* univ. ref.: it binds to lvalues as well as rvalues (temporaries)*/ t) {
    // here `t` is an lvalue, so it doesn't know whether it is bound to a temporary;
    // `T` encodes this missing info, but sadly we're not making `some_func` aware of it,
    // therefore `some_func` will not be able to steal resources from `t` if `t`
    // is bound to a temporary, because it has to leave lvalues intact
    some_func(t);
}
Run Code Online (Sandbox Code Playgroud)

进入这个

template<typename T>
void wrapper(T&& /* univ. ref.: it binds to lvalues as well as rvalues (temporaries)*/ t) {
    // here `t` is an lvalue, so it doesn't know whether it is bound to a temporary;
    // `T` encodes this missing info, and we do use it:
    // `t` bound to lvalue => `T` is lvalue ref => `std::forward` forwards `t` as lvalue
    // `t` bound to rvalue => `T` is non-ref    => `std::forward` turns `t` into rvalue
    some_func(std::forward<T>(t));
}
Run Code Online (Sandbox Code Playgroud)

这是std::forward来自同一本书的 C++14 实现:

template<typename T>
T&& forward(std::remove_reference_t<T>& t) {
    return static_cast<T&&>(t);
}
Run Code Online (Sandbox Code Playgroud)

由此我们可以观察到以下几点std::forward

  • 它是一个模板函数,所以它适用于任何类型T
  • 它通过对无引用的左值引用获取其唯一参数T;请注意,由于参考折叠(参见此处),std::remove_reference_t<T>&解析为与解析为完全相同的内容T&;然而...
  • ... ...的原因std::remove_reference_t<T>&是用来代替T&正好T非推测的情况下(见这里),从而禁用模板类型推演,让你不得不通过指定模板参数<…>
  • 这也意味着它std::forward只不过是static_cast根据您必须传递给的模板参数自动确定的模板参数(通过引用折叠)std::forward
  • 通过使用右值引用或左值引用类型(而不是非引用类型)作为返回类型,它返回右值或左值而不进行任何复制;它通过依赖于应用的引用折叠来实现T&&,其中T您作为模板参数传递给的std::forward那个T是:如果这是一个非引用,那么它T&&是一个右值引用,而如果T是一个左值引用,那么它T&&也是一个左值引用.


归档时间:

查看次数:

38245 次

最近记录:

7 年,6 月 前