【问题标题】:Why does std::move take a forward reference?为什么 std::move 需要前向引用?
【发布时间】:2015-05-10 06:28:44
【问题描述】:

std::move的实现基本上是这样的:

template<typename T>
typename std::remove_reference<T>::type&&
move(T&& t)
{
    return static_cast<typename std::remove_reference<T>::type&&>(t);
}

注意std::move的参数是一个通用引用(也称为转发引用,但我们这里不转发)。也就是说,你可以std::move 左值和右值:

std::string a, b, c;
// ...
foo(std::move(a));       // fine, a is an lvalue
foo(std::move(b + c));   // nonsense, b + c is already an rvalue

但既然std::move 的全部意义在于转换为右值,为什么我们甚至允许std::move 右值?如果std::move 只接受左值不是更有意义吗?

template<typename T>
T&&
move(T& t)
{
    return static_cast<T&&>(t);
}

那么无意义的表达式std::move(b + c) 会导致编译时错误。

std::move 的上述实现对于初学者来说也更容易理解,因为代码完全按照它的样子做:它接受一个左值并返回一个右值。您不必了解通用引用、引用折叠和元函数。

那么为什么std::move 被设计为同时接受左值和右值?

【问题讨论】:

  • 我的快速猜测是,当您编写代码来移动事物时,您不想不断地思考您的变量是左值还是右值,您只想这样做。
  • 因为它旨在无条件地将左值和右值转换为右值。
  • @6502 咆哮不属于堆栈溢出,你同意吗?
  • 在通用代码中,有时您不知道表达式是左值还是右值(例如函数调用),但可能希望无条件地从它移开。此外,允许准身份右值到右值转换并不危险,因此没有令人信服的理由来禁止它。但我怀疑只有 Howard Hinnant 知道真正的原因......
  • @Galik 这有点误解。当您在一些template &lt;T&gt; f(T&amp;&amp; x) 中应用std::move(x) 时,x 是右值引用类型的左值(如果传递了右值)。因此,在这种情况下,您实际上将 std::move 应用于左值。但是我猜想在执行std::move(f()) 之类的操作时会出现问题,您可能不知道f 返回的是左值引用还是右值。

标签: c++ c++11 move-semantics rvalue forwarding-reference


【解决方案1】:

下面是一些简化到极致的例子:

#include <iostream>
#include <vector>

template<typename T>
T&& my_move(T& t)
{
    return static_cast<T&&>(t);
}

int main() 
{
    std::vector<bool> v{true};

    std::move(v[0]); // std::move on rvalue, OK
    my_move(v[0]);   // my_move on rvalue, OOPS
}

上述情况可能出现在通用代码中,例如,当使用具有返回代理对象(右值)的特化的容器时,您可能不知道客户端是否会使用特化,因此您希望无条件支持移动语义。

【讨论】:

【解决方案2】:

不疼。

您只是在建立一个保证,即代码会将结果视为右值。您当然可以编写 std::move 以在处理已经是右值的东西时出错,但是有什么好处呢?

在通用代码中,你不一定知道你将使用什么类型,你会从一堆“如果类型是右值不做任何其他标准:当您可以简单地说“我保证我们可以将其视为右值”时,“:move”无处不在。

你自己说的,只不过是演员表而已。如果参数已经与预期类型匹配,*_cast 操作是否也会失败?

【讨论】:

  • 最后一句话很有说服力。好工作。 +1
猜你喜欢
  • 2012-07-28
  • 1970-01-01
  • 1970-01-01
  • 2017-06-11
  • 2014-02-16
  • 2014-12-19
  • 2019-07-23
  • 1970-01-01
相关资源
最近更新 更多