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.