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

时间:2015-03-09 06:25:04

标签: c++ c++11 move-semantics rvalue forwarding-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 rvalues?如果std::move只接受左值,那么它会更有意义吗?

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

然后,无意义的表达式std::move(b + c)将导致编译时错误。

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

那么为什么std::move设计为同时采用左值和左值?

2 个答案:

答案 0 :(得分:9)

以下是一些简化为极端的示例:

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

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

答案 1 :(得分:2)

没有伤害。

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

在通用代码中,你不一定知道你将要使用什么类型,你会从一堆“如果类型是rvalue什么都不做std中提取出来的表现力增益: :当你可以简单地说“我保证我们可以把它想象成一个左值”时,“移动”到处涂抹。“

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