为什么 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 操作是否也应该失败?
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 操作是否也应该失败?