std::move和std::forward

Posted chen-cs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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源码分析

引用叠加规则和模板参数类型推导规则

 

以上是关于std::move和std::forward的主要内容,如果未能解决你的问题,请参考以下文章

std::move()和std::forward()

std::move和std::forward

C++的std::move与std::forward原理大白话总结

std::move与std::forward

std::move与std::forward

std::move与std::forward