【C++11】万能引用与完美转发

文章目录

  • 1. 模板中的&&—万能引用
  • 2. 完美转发及其应用场景
  • 3. 用到的代码
    • 3.1 string.h
    • 3.2 list.h
    • 3.3 test.cpp

1. 模板中的&&—万能引用

首先我们来看这样一段代码:

【C++11】万能引用与完美转发_第1张图片
这里有4个函数,我们很容易能看出来它们是一个重载的关系
然后我们给这样一个函数模板
【C++11】万能引用与完美转发_第2张图片
大家看这个函数模板的参数,T&& t

这里有两个&&,所以它是右值引用吗?

不是的!
模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
我们实例化这个函数模板的时候
【C++11】万能引用与完美转发_第3张图片
可以传左值,也可以传右值。
我们传的是左值,那参数t就是左值引用,我们传的是右值,参数t就是右值引用。
所以有些地方也把它叫做引用折叠,就是我们传左值的时候,它好像就把&&折叠了一下一样。
但是,大家看到我们这里接收t之后又往下传了一层
【C++11】万能引用与完美转发_第4张图片
那大家就要思考一下在PerfectForward函数内部t又往下传给了Fun,那传给Fun的话t会匹配什么呢?

那我们来运行一下程序看看结果:

【C++11】万能引用与完美转发_第5张图片
欸!怎么回事啊?
为什么全部匹配的都是左值引用啊!

那这里为什么会这样呢?

还记不记得上一篇文章里面又给大家提过一个东西:

就是右值不能取地址,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址
例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地
址,也可以修改rr1。
【C++11】万能引用与完美转发_第6张图片
那可以取地址、可以赋值的话他不就变成左值了嘛。
所以,一个右值被右值引用后属性会变成左值
那想一想其实这样设计也是合理的:
比如这个场景
【C++11】万能引用与完美转发_第7张图片
转移资源也可以认为是修改它了,而临时变量或匿名对象这样将亡值是不能修改的。

那再回过头来看我们上面的问题,大家就应该明白了

【C++11】万能引用与完美转发_第8张图片
即使我们传过去的是右值,那它被右值引用之后也会变成左值,所以里面再传给Fun都匹配的是左值。

所以说:

模板的万能引用只是提供了能够同时接收左值和右值的能力,作用就是限制了接收的类型,但在后续使用中都退化成了左值。
但是有些场景下我们希望能够在传递过程中保持它的左值或者右值的属性,那要如何做到呢?
就需要用我们下面学习的完美转发

2. 完美转发及其应用场景

首先我们来看一个对应的场景:

我们之前模拟实现过list,搞一份过来
【C++11】万能引用与完美转发_第9张图片
有些用不到的东西就给它删了。
配合我们自己搞的那个string,把string里面我们添进去的移动拷贝和移动构造我也先注释掉
我写这样一段代码
【C++11】万能引用与完美转发_第10张图片
那我们之前的实现并没有移动语义,所以
【C++11】万能引用与完美转发_第11张图片
全是深拷贝,不论左值还是右值。
那我们把string的移动构造和移动拷贝放出来,然后我给list的push_back增加右值引用的版本
【C++11】万能引用与完美转发_第12张图片
但是我们的push_back复用了insert,所以insert也增加右值引用版本
【C++11】万能引用与完美转发_第13张图片
再来运行
【C++11】万能引用与完美转发_第14张图片
欸,怎么还不行,list的push_back还是都是深拷贝啊

问题出在哪里呢?

,是不是就是我们上面提到的问题啊!
右值被右值引用后就变成了左值。
在第一次传递给push_back 的参数,右值的话就调用右值引用版本的push_back ,但是push_back里面调用insert第二次传递,就变成左值了
【C++11】万能引用与完美转发_第15张图片
所以最终不论是右值还是左值的push_back,最终调的insert都是左值版本的,所以都是深拷贝

那如何解决这个问题呢?

如何在在传递过程中保持它的左值或者右值的属性呢?
这就要用到完美转发
std::forward 完美转发在传参的过程中保留对象原生类型属性
【C++11】万能引用与完美转发_第16张图片
也是库里面提供的一个函数模板
那我们直接调用forward来保持参数的原生属性
【C++11】万能引用与完美转发_第17张图片
那我们再来运行
【C++11】万能引用与完美转发_第18张图片
哎呀,怎么还不行?
,因为下面其实还有一层传递
【C++11】万能引用与完美转发_第19张图片
insert里面创建新结点的时候要调用node的构造函数
【C++11】万能引用与完美转发_第20张图片
所以这里也要加forward
但是!
【C++11】万能引用与完美转发_第21张图片
node的构造函数我们只有左值引用的版本
所以,我们要再增加一个右值引用的版本
【C++11】万能引用与完美转发_第22张图片
并且,这里_data的初始化我们也要用forward保持x它的属性,因为我们现在存string,他会调string的构造,这里保持它是右值,才会调到右值引用版本的移动拷贝
那这下
【C++11】万能引用与完美转发_第23张图片
就可以了,右值的push_back就是移动拷贝了

那有了完美转发我们最开始那个场景:

【C++11】万能引用与完美转发_第24张图片
都匹配的是右值引用的版本
怎么办?
加个完美转发就可以了
【C++11】万能引用与完美转发_第25张图片

3. 用到的代码

3.1 string.h

#pragma once

namespace bit
{
	class string
	{
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}

		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			//cout << "string(char* str)" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		// s1.swap(s2)
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;
			string tmp(s._str);
			swap(tmp);
		}
		// 移动构造
		string(string&& s)
			:_str(nullptr)
		{
			cout << "string(string&& s) -- 移动拷贝" << endl;
			swap(s);
		}
		// 赋值重载
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 深拷贝" << endl;
			string tmp(s);
			swap(tmp);
			return *this;
		}
		// 移动赋值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动赋值" << endl;
			swap(s);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(char ch)
		{
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		//string operator+=(char ch)
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string operator+(char ch)
		{
			string tmp(*this);
			tmp += ch;
			return tmp;
		}
		const char* c_str() const
		{
			return _str;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};
	string to_string(int value)
	{
		bool flag = true;
		if (value < 0)
		{
			flag = false;
			value = 0 - value;
		}
		string str;
		while (value > 0)
		{
			int x = value % 10;
			value /= 10;
			str += ('0' + x);
		}
		if (flag == false)
		{
			str += '-';
		}
		std::reverse(str.begin(), str.end());
		return str;
	}
}

3.2 list.h

#pragma once
#include

namespace yin
{
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _data;

		list_node(const T& x = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _data(x)
		{}

		list_node(T&& x = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _data(forward<T>(x))
		{}
	};


	// 1、迭代器要么就是原生指针
	// 2、迭代器要么就是自定义类型对原生指针的封装,模拟指针的行为
	template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> node;
		typedef __list_iterator<T, Ref, Ptr> self;
		node* _node;

		__list_iterator(node* n)
			:_node(n)
		{}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}

		self& operator++()
		{
			_node = _node->_next;

			return *this;
		}

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;

			return *this;
		}

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};


	template<class T>
	class list
	{
		typedef list_node<T> node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;

		iterator begin()
		{
			//iterator it(_head->_next);
			//return it;
			return iterator(_head->_next);
		}

		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}

		const_iterator end() const
		{
			//iterator it(_head->_next);
			//return it;
			return const_iterator(_head);
		}

		void empty_init()
		{
			_head = new node(T());
			_head->_next = _head;
			_head->_prev = _head;
		}

		list()
		{
			empty_init();
		}

		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			empty_init();

			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}

		list(const list<T>& lt)
		{
			empty_init();

			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

		// lt1 = lt3
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				//it = erase(it);
				erase(it++);
			}
		}

		void push_back(const T& x)
		{
			insert(end(), x);
		}

		void push_back(T&& x)
		{
			insert(end(), forward<T>(x));
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

		void insert(iterator pos, const T& x)
		{
			node* cur = pos._node;
			node* prev = cur->_prev;

			node* new_node = new node(x);

			prev->_next = new_node;
			new_node->_prev = prev;
			new_node->_next = cur;
			cur->_prev = new_node;
		}

		void insert(iterator pos, T&& x)
		{
			node* cur = pos._node;
			node* prev = cur->_prev;

			node* new_node = new node(forward<T>(x));

			prev->_next = new_node;
			new_node->_prev = prev;
			new_node->_next = cur;
			cur->_prev = new_node;
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());

			node* prev = pos._node->_prev;
			node* next = pos._node->_next;

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;

			return iterator(next);
		}
	private:
		node* _head;
	};
}

3.3 test.cpp

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }

template<typename T>
void PerfectForward(T&& t)
{
	Fun(forward<T>(t));
}

int main()
{
	PerfectForward(10); // 右值
	int a;
	PerfectForward(a); // 左值
	PerfectForward(move(a));// 右值

	const int b = 8;
	PerfectForward(b);// const 左值
	PerfectForward(move(b)); // const 右值

	return 0;
}

//#include "list.h"
//
//int main()
//{
//	yin::list lt;
//
//	bit::string s1("hello world");
//	lt.push_back(s1);
//
//	lt.push_back(bit::string("hello world"));
//	lt.push_back("hello world");
//
//	return 0;
//}

【C++11】万能引用与完美转发_第26张图片

你可能感兴趣的:(C++入门到起飞,c++,万能引用,完美转发,引用折叠)