10

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

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

请注意,参数ofstd::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设计为同时采用左值和右值呢?

4

2 回答 2

12

这是一些简化到极端的示例:

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

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

于 2015-03-09T07:28:47.563 回答
4

它不疼。

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

在通用代码中,你不一定知道你将使用什么类型,你会从一堆“如果类型是右值不做任何其他 std::move”中提取什么表现力当您可以简单地说“我保证我们可以将其视为右值”时,到处都贴满了。

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

于 2015-03-09T12:27:00.210 回答