在学习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 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));
}
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 &,也就是左值引用。
上面三类就是引用折叠的定义。
完美转发:
所谓的完美转发,就是当我传左值和右值引用到模板函数中时,在模板函数中使用这个参数,要能够区分左值和右值引用,在使用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));
}
int main()
{
int a = 10;
string s = "asd";
forwardTest(a);
cout << "------------------" << endl;
forwardTest(move(s));
}
在forwardTest函数中,第一个print函数的调用始终是左值,这是因为无论是左值还是右值,进入到函数中时,都会变成左值,为什么呢?因为它有了地址,所以就是左值了,所以为了解决这种问题,std:forward就开始显示它的作用了,我们需要利用到forward进行转发,让传进去的值,可以按照其左右值的类型进行分发,类似于上面代码的print,两个重载函数,可以根据参数类型进行对应的分发。
|