先解释一下什么是不完美的转发
    右值引用2 完美转发 - 图1

    1. int a = 0;
    2. //process内容cout一句话 forward内容cout一句话并调用process
    3. void forward(int&& i){cout<<”xxx”<<endl; process(i);}
    4. process(a);//调用void process(int&i) a被视为左值
    5. process(1);//调用 void process(int&& i) 1是字面值 是匿名的是个右值 调用右值引用的版本
    6. process(move(a));//调用 void process(int&& i) 通过move强制将左值a变为右值 调用函数的右值版本
    7. forward(2);//1 调用forward(int&& i) 因为2是右值 2 调用process(int&)

    问什么不调用process(int&& i),因为2是个右值被复制给了形参int&& i,i的类型是右值引用但是i是有名的(named object)!!! i变量是左值!!!, 在调用process时是 process(i);这样来调用i是左值所以肯定调用的是process的左值引用版本process(int&)。
    所以我们上面说c.insert(ite,Vtype(buf)); 是个不完美转发,是先调用insert(…,&&x),给匿名对象 Vtype(buf)赋名x,接下来因为x有名了所以insert内以x做形参的其他函数都不会调用&&x右值引用版本,但我们本意是 x就是右值,函数内以x为形参的函数都应该将x当作右值,调用x为右值的函数版本。
    形参为匿名对象赋名导致其成为左值 就是不完美转交的根本原因。

    1. forward(move(a)); //1 调用forward(int&& i) 因为move(a)是右值 2 调用process(int&)
    2. //因为是process(i)有名地调用i所以调用的是process的左值引用版本process(int&)原因和上面的一样
    3. forward(a);//编译出错 forward只写了 右值引用版本但是a是有名的,但又没写forward的其他版本所以出错
    4. const int&b=1;//常引用 绑定一个常量
    5. process(b);//编译出错 缺少process(const int&)版本
    6. process(move(b));//编译出错 缺少process(std::remove_reference<const int&>::type)版本


    Perfect Forwarding(完美转发)
    自己在写右值移动版本的函数时要注意这点

    1. template<typename T1,typename T2>
    2. void functionA(T1&& t1,T2&& t2)
    3. {
    4. functionB(std::foward<T1>(t1),std::forward<T2>(t2));//这样t1,t2将依旧保持右值身份
    5. }

    右值引用2 完美转发 - 图2
    std::forward源码

    1. // 函数重载 传入左值版本
    2. template<typename _Tp>
    3. constexpr _Tp&& foward(typename std::remove_reference<_Tp>::type& __t)noexcept
    4. {return static_cast<_Tp&&>(__t);}//forward传入的是左值type& 将传入变量的类型强制变为右值引用类型
    5. // 函数重载 传入右值版本
    6. template<typename _Tp>
    7. constexpr _Tp&& foward(typename std::remove_reference<_Tp>::type&& __t)noexcept
    8. {
    9. static_assert(!std::is_lvalue_reference<_Tp>::value,”_Tp is an lvalue reference”);
    10. return static_cast<_Tp&&>(__t);
    11. }//forward传入的是右值type&& 将传入变量的类型强制变为右值引用类型

    在调用move之前调用forward



    右值引用2 完美转发 - 图3
    右值引用2 完美转发 - 图4
    move aware class

    1. class MyString{
    2. public:
    3. static size_t Dctor;//默认构造调用次数
    4. static size_t Cctor;//总构造调用次数
    5. static size_t CCctor;//拷贝构造调用次数
    6. static size_t CAsgn;//拷贝赋值调用次数
    7. static size_t Mctor;//移动构造调用次数
    8. static size_t MAsgn;//移动赋值调用次数
    9. static size_t Dtor;//总析构调用次数
    10. private:
    11. char* _data;
    12. size_t _len;
    13. void _init_data(const char*s){//对字符串s 深拷贝
    14. _data = new char[_len+1];
    15. memcpy(_data,s,_len);
    16. _data[_len] = \0’;
    17. //为data申请s那么大的空间+1 并将s内容拷贝到那个堆上内存上
    18. }
    19. public:
    20. MyString():_data(NULL),_len(0){++DCtor;}//默认构造
    21. MyString(const char* p):_len(strlen(p))//带参数的构造
    22. {
    23. ++Ctor;
    24. _init_data(p);
    25. }
    26. MyString(const MyString& str):_len(str.len)//拷贝构造
    27. {
    28. ++CCtor;
    29. _init_data(str._data);//拷贝
    30. }
    31. MyString(MyString&& str)noexcept //移动构造
    32. :_data(str._data),_len(str.len)
    33. {
    34. ++MCtor;
    35. //this偷了str的_data后,str不能再使用data了 所以需要将相关信息清空
    36. str._len = 0;
    37. str._data = NULL;
    38. }
    39. 添加
    40. //拷贝赋值
    41. MyString& operator=(const MyString& str)
    42. {
    43. ++CAsgn;
    44. if(this!=&str)//自我赋值检查
    45. {
    46. if(_data)delete _data;//先删除 this data之前存的数据
    47. _len = str._len;
    48. _init_data(str._data);
    49. }
    50. return *this;
    51. }
    52. //移动赋值
    53. MyString& operator=(MyString&& str) noexcept
    54. {
    55. ++MAsgn;
    56. if(this!=&str)//自我赋值检查
    57. {
    58. if(_data) delete _data;// dctor默认是不抛出错误的 是noexcept的
    59. //偷str的_data
    60. _len = str._len;
    61. _data = str._data;
    62. //this偷了str的_data后,str不能再使用data了 所以需要将相关信息清空
    63. str._len = 0;
    64. str._data = nullptr;
    65. }
    66. return *this;
    67. }
    68. virtual ~MyString{
    69. ++Dtor;
    70. if(_data)delete _data;
    71. }
    72. char* get() const {return _data;}
    73. //如果想要将这个类对象装进set容器 需要重载< 和 ==
    74. bool operator<(const MyString& rhs)const{...}
    75. bool operator==(const MyString& rhs)const{...}
    76. };
    77. size_t MyString::Dctor = 0;//默认构造调用次数
    78. size_t MyString::Cctor = 0;//总构造调用次数
    79. size_t MyString::CCctor = 0;//拷贝构造调用次数
    80. size_t MyString::CAsgn = 0;//拷贝赋值调用次数
    81. size_t MyString::Mctor = 0;//移动构造调用次数
    82. size_t MyString::MAsgn = 0;//移动赋值调用次数
    83. size_t MyString::Dtor = 0;//总析构调用次数
    84. //为了unordered容器可以装这个对象 还需要写自己的哈希计算函数
    85. namespace std{//必须放在std里
    86. template<>
    87. struct hash<MyString>{
    88. size_t operator()(const MyString& s)const noexcept{.....}
    89. };

    move aware对vector影响很大 对list,multiset红黑树,unorder_multiset哈希表—内容以节点形式存在 影响小

    右值引用2 完美转发 - 图5
    M c1(c);//M是vector<MyString>()容器 — 这里直接调用的是vector这个类的拷贝构造函数
    先为this分配对应的空间,再将传入的vector&内的元素内一个一个拷贝到this
    所以很耗时 秒级
    右值引用2 完美转发 - 图6
    M c2(move(c));//M是vector<MyString>()容器 — 这里直接调用的是vector这个类的移动构造
    核心操作是 _M_swap_data 内调用std::swap将一些vector内的3根指针进行交换 因为c2的这些指针无意义 所以在交换后c获得这些无意义指针,所以在交换后不能再使用c
    因为只是交换3根指针,所以移动构造非常快 微秒级

    除非设计不允许移动,STL类大都支持移动语义函数,即可移动的。 另外,编译器会默认在用户自定义的class和struct中生成移动语义函数,但前提是用户没有主动定义该类的拷贝构造等函数(具体规则自行百度哈)。 因此,可移动对象在<需要拷贝且被拷贝者之后不再被需要>的场景,建议使用std::move触发移动语义,提升性能。
    还有些STL类是move-only的,比如unique_ptr(我们写的smart_ptr),这种类只有移动构造函数,因此只能移动(转移内部对象所有权,或者叫浅拷贝),不能拷贝(深拷贝)