move、forward与右值引用
首先看主函数代码:
int main()
{
CMyString str1 = "aaa";
vector<CMyString> vec;
//预留10个空间
vec.reserve(10);
cout << "-----------------------" << endl;
vec.push_back(str1);
vec.push_back(CMyString("bbb")); // move forword
cout << "-----------------------" << endl;
return 0;
}
主要看两条绿线之间的内容,其中vec.push_back(str1);
匹配的是string的左值拷贝构造,而vec.push_back(CMyString("bbb"));
将匹配右值拷贝构造,这究竟是怎么做到的呢?
我们现在来看vector的push_back方法的代码:
template<typename Ty> // 函数模板的类型推演 + 引用折叠
void push_back(Ty &&val) //Ty CMyString& + && = CMyString&
{
if (full())
expand();
// move(左值):移动语义,得到右值类型 (int&&)a
// forward:类型完美转发,能够识别左值和右值类型
_allocator.construct(_last, std::forward<Ty>(val));
_last++;
}
首先来看参数类型Ty,Ty当匹配到左值语义,也就是string &,此时val的类型是string& &&为左值+右值。此时编译器会根据以下规则进行优化:
左值+右值=左值;
右值+右值=右值;
也就是,string& &&当成string&来看,string&& &&当成string&&来看。
但是记住:接收一个右值引用变量本身还是一个左值。
所以push_back中的val还是一个左值,这样_allocator.construct(_last, val);
的空间适配又是一个问题。
move && forward
所以此时需要引入move和forward语句。
move:移动语义,得到右值类型
forward:类型完美转发,能够识别左值和右值类型
move可以看成将左值强转成右值,具体用法是:_allocator.construct(_last, std::move<Ty>(val));
forward可以看成move的pro plus版本,他可以根据类型识别具体需要哪个,如果是右值类型,那么就匹配_allocator的右值,具体用法是:_allocator.construct(_last, std::forward<Ty>(val));
我们来看一下_allocator的定义:
template<typename Ty>
void construct(T *p, Ty &&val)
{
new (p) T(std::forward<Ty>(val));
}
其中用的便是forward。move此时就显得有点笨逼。
参考文献
[1] 施磊.腾讯课堂——C++高级.图论科技,2020.7.