重温 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(...)
,就肯定不会出现这种异常的转换。