重温 std::move 以及 std::forward
其实本来是另外一篇 curry 函数的一部分,但是和主题没什么关系,就拆出来了。
std::move
1 | template<typename _Tp> |
std::move 做的事就是把类型 static_cast
成对应的右值类型。
通常我们希望 T a = std::move(b) 调用了 T 的
move 构造函数,但是如果 b 是带有
const 修饰符的,那么 std::move
的结果的类型就会变成 const T&&,于是调用的就是
copy 构造函数了(const T&& 无法转换成
T&&,但是可以转换成
const T&)。
std::forward
1 | template<typename _Tp> |
由于 std::forward 通常用在 universal reference
中,比如下面的代码:
1 | void f(const std::string&); |
假如 arg的类型 T&&:
- 右值引用,如
string&&,那么T就是string,std::forward的返回类型就是string&&。 - 左值引用,如
string&,那么T就是string&,std::forward的返回类型是string&。(由于引用折叠 reference collapsing 的存在,导致string& && = string&。)
可见 std::forward 就是把类型 static_cast
成作为函数参数传入时的类型。也许你会问,这不是啥也没干吗?其实对于传入右值引用的情况有所变化,它在函数内用起来就像是左值引用(因为任何有名字的变量必定是左值),有一个简单的例子。
1 | void f(std::string&& x) { |
根据 std::forward 的源代码,可以看到它用
static_assert 防止了一种转换,就是 T
为左值引用的情况,此时返回值也是左值引用,但是参数是右值。显然,把右值
cast 成左值是不合法的,因为会导致 dangling reference,比如:
1 | int& x = std::forward<int&>(1); |
当然,如果用通常的方法使用,也就是配合模板或者
auto + decltype(...),就肯定不会出现这种异常的转换。