先解释一下什么是不完美的转发
int a = 0;//process内容cout一句话 forward内容cout一句话并调用processvoid forward(int&& i){cout<<”xxx”<<endl; process(i);}process(a);//调用void process(int&i) a被视为左值process(1);//调用 void process(int&& i) 1是字面值 是匿名的是个右值 调用右值引用的版本process(move(a));//调用 void process(int&& i) 通过move强制将左值a变为右值 调用函数的右值版本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为右值的函数版本。
形参为匿名对象赋名导致其成为左值 就是不完美转交的根本原因。
forward(move(a)); //1 调用forward(int&& i) 因为move(a)是右值 2 调用process(int&)//因为是process(i)有名地调用i所以调用的是process的左值引用版本process(int&)原因和上面的一样forward(a);//编译出错 forward只写了 右值引用版本但是a是有名的,但又没写forward的其他版本所以出错const int&b=1;//常引用 绑定一个常量process(b);//编译出错 缺少process(const int&)版本process(move(b));//编译出错 缺少process(std::remove_reference<const int&>::type)版本
Perfect Forwarding(完美转发)
自己在写右值移动版本的函数时要注意这点
template<typename T1,typename T2>void functionA(T1&& t1,T2&& t2){functionB(std::foward<T1>(t1),std::forward<T2>(t2));//这样t1,t2将依旧保持右值身份}

std::forward源码
// 函数重载 传入左值版本template<typename _Tp>constexpr _Tp&& foward(typename std::remove_reference<_Tp>::type& __t)noexcept{return static_cast<_Tp&&>(__t);}//forward传入的是左值type& 将传入变量的类型强制变为右值引用类型// 函数重载 传入右值版本template<typename _Tp>constexpr _Tp&& foward(typename std::remove_reference<_Tp>::type&& __t)noexcept{static_assert(!std::is_lvalue_reference<_Tp>::value,”_Tp is an lvalue reference”);return static_cast<_Tp&&>(__t);}//forward传入的是右值type&& 将传入变量的类型强制变为右值引用类型
在调用move之前调用forward


move aware class
class MyString{public:static size_t Dctor;//默认构造调用次数static size_t Cctor;//总构造调用次数static size_t CCctor;//拷贝构造调用次数static size_t CAsgn;//拷贝赋值调用次数static size_t Mctor;//移动构造调用次数static size_t MAsgn;//移动赋值调用次数static size_t Dtor;//总析构调用次数private:char* _data;size_t _len;void _init_data(const char*s){//对字符串s 深拷贝_data = new char[_len+1];memcpy(_data,s,_len);_data[_len] = ‘\0’;//为data申请s那么大的空间+1 并将s内容拷贝到那个堆上内存上}public:MyString():_data(NULL),_len(0){++DCtor;}//默认构造MyString(const char* p):_len(strlen(p))//带参数的构造{++Ctor;_init_data(p);}MyString(const MyString& str):_len(str.len)//拷贝构造{++CCtor;_init_data(str._data);//拷贝}MyString(MyString&& str)noexcept //移动构造:_data(str._data),_len(str.len){++MCtor;//this偷了str的_data后,str不能再使用data了 所以需要将相关信息清空str._len = 0;str._data = NULL;}添加//拷贝赋值MyString& operator=(const MyString& str){++CAsgn;if(this!=&str)//自我赋值检查{if(_data)delete _data;//先删除 this data之前存的数据_len = str._len;_init_data(str._data);}return *this;}//移动赋值MyString& operator=(MyString&& str) noexcept{++MAsgn;if(this!=&str)//自我赋值检查{if(_data) delete _data;// dctor默认是不抛出错误的 是noexcept的//偷str的_data_len = str._len;_data = str._data;//this偷了str的_data后,str不能再使用data了 所以需要将相关信息清空str._len = 0;str._data = nullptr;}return *this;}virtual ~MyString{++Dtor;if(_data)delete _data;}char* get() const {return _data;}//如果想要将这个类对象装进set容器 需要重载< 和 ==bool operator<(const MyString& rhs)const{...}bool operator==(const MyString& rhs)const{...}};size_t MyString::Dctor = 0;//默认构造调用次数size_t MyString::Cctor = 0;//总构造调用次数size_t MyString::CCctor = 0;//拷贝构造调用次数size_t MyString::CAsgn = 0;//拷贝赋值调用次数size_t MyString::Mctor = 0;//移动构造调用次数size_t MyString::MAsgn = 0;//移动赋值调用次数size_t MyString::Dtor = 0;//总析构调用次数//为了unordered容器可以装这个对象 还需要写自己的哈希计算函数namespace std{//必须放在std里template<>struct hash<MyString>{size_t operator()(const MyString& s)const noexcept{.....}};
move aware对vector影响很大 对list,multiset红黑树,unorder_multiset哈希表—内容以节点形式存在 影响小

M c1(c);//M是vector<MyString>()容器 — 这里直接调用的是vector这个类的拷贝构造函数
先为this分配对应的空间,再将传入的vector&内的元素内一个一个拷贝到this
所以很耗时 秒级
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),这种类只有移动构造函数,因此只能移动(转移内部对象所有权,或者叫浅拷贝),不能拷贝(深拷贝)
