C++ 中的万能引用、引用折叠、完美转发

在学习c++过程中,相信不少同学都或多或少的听过万能引用、引用折叠、完美转发,介绍这几个概念之前,首先列举下左值和右值的概念;

左值和右值:

顾名思义,可以简单的理解为在等号左边的值是左值,再等号右边的值是右值,例如 int a = 1,那个a就是左值,而1就是右值,左值可以用&标志符取地址,右值不行;另外,临时变量也是属于右值,例如:

using namespace std;
class A
{
    
    

};

int main()
{
    
    
	A a = A();
}

这里a是左值,A()是属于临时变量,是属于右值

左值引用和右值引用:

左值引用用一个&符号进行标志,例如

int main()
{
    
    
	int a = 10;
	int & left_value = a;
	//或者用const修饰
	const int& left_value1 = 100;
}

这里left_value就是左值引用, left_value1也是左值引用,由于有const进行修饰,所以可以直接赋右值;


右值引用用标志符号&&表示,例如

int main()
{
    
    
	int&& a = 100;
}

这里right_value就是右值引用。
这里小结一下,其实无论是左值、右值还是左值引用、右值引用,都是一种类型,就可我们平时定义类型int float、double等一样,都对应着一种定义,知道他们是不用的类型就可以。

万能引用:

万能引用通常是在模板中使用,例如T &&这种模板的定义,它既可以接受左值,右值,也可以接收左值引用、右值引用作为参数,所以成为万能引用,而且在使用的过程中,我还发现这种可以不用给定模板类型,说明使用万能引用可以实现自动推断,如下面的例子:

#include<iostream>
using namespace std;

template<typename T>
void print(T& a)  // 接受左值和左值引用
{
    
    
	cout << "左值" << endl;
}

template<typename T>
void print(T&& a) //接受右值和右值引用
{
    
    
	cout << "右值" << endl;
}

template<typename T>
void forwardTest(T&& v)
{
    
    
	print(v);
	print(move(v));
	print(forward<T>(v)); //利用std::forward<T>(u)进行转发,当且仅当T为左值引用时,u被转换为左值
	//引用,否者全都转换为右值引用
}

int main()
{
    
    
	int a = 10;
	string s = "asd";
	forwardTest(a);
	cout << "------------------" << endl;
	forwardTest(move(s));
}

/*
输出为:
左值
右值
左值
------------------
左值
右值
右值*/

在模板函数 forwardTest中,模板参数类型是T && ,这个就是万能引用的标志,然后我们可以用这个函数把穿进去参数,根据左值和右值进行完美转发。

引用折叠:

所谓的引用折叠,就是当有多个&标志的时候,进行类型的推断,规则是当且仅当T的类型是&& 右值的时候,根据引用折叠推断出来的类型是右值,否者都是左值引用,例如
转换规则是:

  • && && ==> &&
  • & && ==> &
  • && ==>&

或者换种说话就是:

  • 当T的类型是 int &&,代入万能引用模板就是 int && &&,等价于int &&,也就是右值引用。
  • 当T的类型是int &,代入万能引用模板就是 int & && ,等价于iint &,也就是左值引用。
  • 当T的类型是int ,代入万能引用模板就是 int &&,这个等价于int &,也就是左值引用。

上面三类就是引用折叠的定义。

扫描二维码关注公众号,回复: 14796078 查看本文章

完美转发:

所谓的完美转发,就是当我传左值和右值引用到模板函数中时,在模板函数中使用这个参数,要能够区分左值和右值引用,在使用std:forward之前会有什么问题呢,还是上面万能引用中提到的代码,如下:

#include<iostream>
using namespace std;

template<typename T>
void print(T& a)  // 接受左值和左值引用
{
    
    
	cout << "左值" << endl;
}

template<typename T>
void print(T&& a) //接受右值和右值引用
{
    
    
	cout << "右值" << endl;
}

template<typename T>
void forwardTest(T&& v)
{
    
    
	print(v);
	print(move(v));
	print(forward<T>(v)); //利用std::forward<T>(u)进行转发,当且仅当T为左值引用时,u被转换为左值
	//引用,否者全都转换为右值引用
}

int main()
{
    
    
	int a = 10;
	string s = "asd";
	forwardTest(a);
	cout << "------------------" << endl;
	forwardTest(move(s));
}

/*
输出为:
左值
右值
左值
------------------
左值
右值
右值*/

在forwardTest函数中,第一个print函数的调用始终是左值,这是因为无论是左值还是右值,进入到函数中时,都会变成左值,为什么呢?因为它有了地址,所以就是左值了,所以为了解决这种问题,std:forward就开始显示它的作用了,我们需要利用到forward进行转发,让传进去的值,可以按照其左右值的类型进行分发,类似于上面代码的print,两个重载函数,可以根据参数类型进行对应的分发。

猜你喜欢

转载自blog.csdn.net/qq_41841073/article/details/127022216