为什么 std::move 采用前向引用

Why does std::move take a forward reference?

本文关键字:引用 std move 为什么      更新时间:2023-10-16

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被设计为同时采用左值和右值呢?

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

#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
}

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

它不疼。

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

在通用代码中,你不一定知道你将使用什么类型,你会从一堆"如果类型是右值,什么都不做其他std::move"中提取出什么表现力的收益,当你可以简单地说"我保证我们可以把它看作是一个右值"时。

你自己说的,无非是演员阵容。如果参数已与预期类型匹配,则 *_cast 操作是否也应失败?