为什么 std::move 需要前向引用?

Why does std::move take a forward reference?

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 的实现对于初学者来说也更容易理解,因为代码确实如它所显示的那样:它接受一个左值和 returns 一个右值。您不必了解通用引用、引用折叠和元函数。

那么为什么 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
}

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

不痛。

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

在通用代码中,您不一定知道要使用的类型是什么,您会从一堆 "if type is rvalue do nothing else std::move" 中提取出什么样的表现力?你可以简单地说 "I promise we can think of this as an rvalue".

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