std::move和std::forward
std::move和std::forward是C++11中新增的标准库函数,分别用于实现移动语义和完美转发。
下面让我们分析一下这两个函数在gcc4.6中的具体实现。
预备知识
引用折叠规则
T& + & => T&
T&& + & => T&
T& + && => T&
T&& + && => T&&
就是左值引用会传染,沾上一个左值引用就变左值引用了,只有都为纯右值引用结果才为右值引用&& && = &&。
模板参数推导规则
只有调用前fpar是&&,apar是&时,调用后T的实际类型才是A&,其余3种情况下都是A。
1、当模板参数为T时
因为是值传递,所以expr的所有修饰特性都会被忽略,const, 引用,volatile等,都被忽略。
- 如果表达式不是指针,T和x的类型都是非引用类型,cv限定符(const、volatile)去除。比如表达式是int、int&、int&&、const int、const int&,只要不是指针,T和x最终都是int;
- 如果是指针,T和x都是对应的指针类型,cv限定符保留;注意指针常量的情况,由于指针常量本质上是一个常量,所以cv限定符去除。
1 #include <iostream> 2 3 template<typename T> 4 void f(T param) { 5 T a; 6 std::cout << param; 7 }// param 为值传递 8 // 因为是值传递,所以expr的所有修饰特性都会被忽略,const, 引用,volatile等,都被忽略 9 int main() 10 { 11 int x = 2; 12 const int cx = x; 13 int& rx = x; 14 const int& crx = x; 15 int* p = &x; 16 const int* cip = &x; 17 int* const icp = &x; 18 f(x); // x: int param : int, T : int 19 f(cx); // cx: const int, param : int, T : int 20 f(rx); // rx : int&, param : int, T : int 21 f(crx); // crx : const int&, param : int, T : int 22 f(p); // p : int*, param : int*, T : int* 23 f(cip); // cip : const int*, param : const int*, T : const int* 24 f(icp); // icp : int* const, param : int*, T : int* 25 26 return 0; 27 }
2、当模板参数为T&时
- 对于T&,不管表达式是什么,parma一定是左值引用类型(表达式是指针则x是指针的引用),cv限定符保留;
- 对于T*,和T&类似,parma一定是指针类型,cv限定符保留;
1 template<typename T> 2 void f(T& param) 3 { 4 T a(0); 5 } 6 7 template<typename T> 8 void pf(T* param) 9 { 10 T a(0); 11 } 12 13 template<typename T> 14 void cf(const T& param) 15 { 16 T a(0); 17 } 18 19 20 int main() 21 { 22 int x = 2; 23 const int cx = x; 24 int& rx = x; 25 const int& crx = x; 26 int* p = &x; 27 const int* cip = &x; 28 int* const icp = &x; 29 int&& rrx = 2; 30 31 f(x); // x: int param : int&, T : int 32 f(cx); // cx: const int, param : const int&, T : const int 33 f(rx); // rx : int&, param : int&, T : int 34 f(crx); // crx : const int&, param : const int&, T : const int 35 f(p); // p : int*, param : int* &, T : int* 36 f(cip); // cip : const int*, param : const int* &, T : const int* 37 f(icp); // icp : int* const, param : int* const &, T : int* const 38 f(rrx); // rrx : int&&, param : int&, T : int 39 40 41 42 43 pf(p); // p : int*, param : int*, T : int 44 pf(cip); // cip : const int*, param : const int*, T : const int 45 pf(icp); // icp : int* const, param : int*, T : int 46 47 48 49 cf(x); // x: int param : const int&, T : int 50 cf(cx); // cx: const int, param : const int&, T : int 51 cf(rx); // rx : int&, param : const int&, T : int 52 cf(crx); // crx : const int&, param : const int&, T : int 53 cf(p); // p : int*, param : int* const&, T : int* 54 cf(cip); // cip : const int*, param : const int* const&, T : const int* 55 cf(icp); // icp : int* const, param : int* const &, T : int* 56 cf(rrx); // rrx : int&&, param : const int&, T : int 57 58 59 return 0; 60 }
3、当模板参数为T&&时
- 不管表达式是什么,parma都是引用类型:
- 如果表达式是左值,则parma是左值引用,cv限定符保留,即采用和T&相同的规则,只不过因为T&&多了个&,为了转为左值引用需要引入引用折叠的概念。通过下面的例子可以发现,T一定是左值引用类型,这是T被推断为引用类型的唯一情况;
- 如果表达式是右值,则x是右值引用类型;
1 template<typename T> 2 void f(T&& x); 3 4 int i; 5 int& r; 6 int&& rr; 7 const int& cr; 8 int* p; 9 const int* const q; 10 // 都是左值,采用的规则和T&的相同 11 f(i); // x是int& => T&&是int&,int& + && = int& => T是int& 12 f(r); // x是int& => T&&是int&,int& + && = int& => T是int& 13 f(rr); // x是int& => T&&是int&,int& + && = int& => T是int& 14 f(cr); // x是const int& => T&&是const int& => T是const int& 15 f(p); // x是int* & => T&&是int* & => T是int* & 16 f(q); // x是const int* const & => T&&是const int* const & => T是const int* const & 17 18 // 表达式是右值 19 f(42); // x是int&& => T&&是int&& => T是int
std::remove_reference
std::remove_reference为C++11标准库中的元函数,其功能为去除类型中的引用。
std::remove_reference<U&>::type ≡ U
std::remove_reference<U&&>::type ≡ U
std::remove_reference<U>::type ≡ U
static_cast<T&&>(t)
以下语法形式将把表达式 t 转换为T类型的右值(准确的说是无名右值引用,是右值的一种)
static_cast<T&&>(t)
无名的右值引用是右值
具名的右值引用是左值。
注:本文中 ≡ 含义为“即,等价于“。
std::move
函数功能
std::move(t) 负责将表达式 t 转换为右值,使用这一转换意味着你不再关心 t 的内容,它可以通过被移动(窃取)来解决移动语意问题。
源码与测试代码
1 template<typename _Tp> 2 inline typename std::remove_reference<_Tp>::type&& 3 move(_Tp&& __t) 4 { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
1 #include<iostream> 2 using namespace std; 3 4 struct X {}; 5 6 int main() 7 { 8 X a; 9 X&& b = move(a); 10 X&& c = move(X()); 11 }
std::forward
函数功能
std::forward<T>(u) 有两个参数:T 与 u。当T为左值引用类型时,u将被转换为T类型的左值,否则u将被转换为T类型右值。如此定义std::forward是为了在使用右值引用参数的函数模板中解决参数的完美转发问题。
源码与测试代码
1 /// forward (as per N3143) 2 template<typename _Tp> 3 inline _Tp&& 4 forward(typename std::remove_reference<_Tp>::type& __t) 5 { return static_cast<_Tp&&>(__t); } 6 7 template<typename _Tp> 8 inline _Tp&& 9 forward(typename std::remove_reference<_Tp>::type&& __t) 10 { 11 static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument" 12 " substituting _Tp is an lvalue reference type"); 13 return static_cast<_Tp&&>(__t); 14 }
1 #include<iostream> 2 using namespace std; 3 4 struct X {}; 5 void inner(const X&) {cout << "inner(const X&)" << endl;} 6 void inner(X&&) {cout << "inner(X&&)" << endl;} 7 template<typename T> 8 void outer(T&& t) {inner(forward<T>(t));} 9 10 int main() 11 { 12 X a; 13 outer(a); 14 outer(X()); 15 inner(forward<X>(X())); 16 } 17 //inner(const X&) 18 //inner(X&&) 19 //inner(X&&)
参考
zwvistaC++11尝鲜:std::move和std::forward源码分析
引用叠加规则和模板参数类型推导规则
菜鸟手记。