栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

【C++】移动语义与完美转发

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【C++】移动语义与完美转发

移动语义与完美转发 1. 右值

C++中表达式分为左值和右值,简单而言,有内存地址的表达式就是左值,它可以出现在赋值语句的左边或者右边。无法取内存地址的表达式是右值,只能出现在赋值语句的右边。 C++中常见的出现右值的情况:常量字面量、函数返回值或对象(左值引用类型除外)、匿名对象。右值引用可以实现移动语义与完美转发。

2. 移动语义

移动构造函数和移动赋值运算符,统称为移动语义。C++11在标准库中引入了std::move函数,它的作用是“将表达式强行转为右值类型”。

#include 
using namespace std;

class CMyString {
public:
	// 有参构造函数
	CMyString(const char* pString)
	{
		m_iLen = strlen(pString) + 1;
		m_pBuffer = new char[m_iLen];
		strcpy_s(m_pBuffer, m_iLen, pString);
	}
	// 拷贝构造函数
	CMyString(CMyString& other)
	{
		m_iLen = other.m_iLen;
		m_pBuffer = new char[m_iLen];
		strcpy_s(m_pBuffer, m_iLen, other.m_pBuffer);
	}
	// 拷贝赋值运算符
	CMyString& operator=(const CMyString& other)
	{
		if (this != &other)
		{
			free();
			m_iLen = other.m_iLen;
			m_pBuffer = new char[m_iLen];
			strcpy_s(m_pBuffer, m_iLen, other.m_pBuffer);
		}
		return *this;
	}
	// 移动构造函数
	CMyString(CMyString&& other) noexcept
	{
		m_iLen = other.m_iLen;
		m_pBuffer = other.m_pBuffer;
		other.m_pBuffer = nullptr;	// 阻止该右值对象析构
	}
	// 移动赋值运算符
	CMyString& operator=(CMyString&& other) noexcept
	{
		if (this != &other)
		{
			free();
			m_iLen = other.m_iLen;
			m_pBuffer = other.m_pBuffer;
			other.m_pBuffer = nullptr;
		}
		return *this;
	}
	// 析构函数
	~CMyString()
	{
		free();
	}
	void free()
	{
		m_iLen = 0;
		if (m_pBuffer) {
			delete[] m_pBuffer;
		}
	}
public:
	char* m_pBuffer;
	int m_iLen;
};

int main()
{
	CMyString str1("hello");
	CMyString str2(move(str1));	// 移动构造函数
	str2 = move(str1);	        // 移动赋值运算符
	system("pause");
	return 0;
}
3. 完美转发

**有名字的右值引用其实是左值!**一个右值引用作为函数参数的形参时,在函数内部转发该参数给内部其他函数时,它就变成一个左值,并不是原来的类型了。如果需要按照参数原来的类型转发到另一个函数,可以使用 C++11 提供的 std::forward () 函数,该函数实现的功能称之为完美转发。

// 函数原型
template  T&& forward (typename remove_reference::type& t) noexcept;
template  T&& forward (typename remove_reference::type&& t) noexcept;
万能引用

对于一个普通函数,它的形参,要么接受左值、要么接受右值类型。C++11 开始,规定了一种特殊的形式,函数形参既可以匹配左值,也可以匹配右值。这种情况必须是模板的形式,并且以&&作为形参数。

引用折叠

为什么有万能引用?在 C++11 之前,是不允许引用的引用存在的。但是 C++11 之后,引用的引用在特定情况下允许存在,他们会在编译时被自动化简为左值引用或者右值引用,化简的过程称为引用折叠。

引用引用折叠
T& &T&
T&& &T&
T& &&T&
T&& &&T&&
#include 
using namespace std;

void fun1(int&) {
	cout << "fun1(int&)" << endl;
}

void fun2(int&&) {
	cout << "fun2(int&&)" << endl;
}

template
void tfun(T&& arg)	// 万能引用
{
	cout << "tfun(T&& arg)" << endl;
}

int main()
{
	int value = 5;
	//fun1(5);		// 错误
	fun1(value);	// 正确
	fun2(5);		// 正确
	//fun2(value);	// 错误

	tfun(5);     // 正确,T -> int, void tfun(int && arg),匹配右值。
	tfun(value); // 正确,T -> int&,void tfun(int& && arg),引用折叠得到void tfun(int& arg),匹配左值。
	system("pause");
	return 0;
}
// 实现完美转发
#include 
using namespace std;

template
void printValue(T& t)
{
	cout << "left-value" << endl;
}

template
void printValue(T&& t)
{
	cout << "right-value" << endl;
}

template
void testForward(T&& value)	// 万能引用
{
	printValue(value);
	printValue(move(value));
	printValue(forward(value));
	cout << endl;
}

int main()
{
	testForward(520);
	int num = 1314;
	testForward(num);
	testForward(forward(num));			
	testForward(forward(num));	
	testForward(forward(num));	
	system("pause");
	return 0;
}

参考:https://www.ttlarva.com/master/learn_cpp/01_rvalue_ref.html
参考:https://subingwen.cn/cpp/move-forward/

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/846984.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号