右值引用
右值和左值是什么
- 普通类型的变量,有名字,可以取地址,都认为是左值。
- const修饰的常量,是左值
- 如果表达式运行结果是单个变量是一个引用则认为是左值。
- 如果表达式的运行结果是一个临时变量或者对象,认为是右值。
需要注意的是int a = 10;这一句里边,a是左值,而10是右值并且是纯右值,因为10只是一个符号,没有具体的空间
C++11对右值进行了严格的区分:
C语言中的纯右值,比如:a+b, 100
将亡值。比如:表达式的中间结果、函数按照值的方式进行返回
右值引用的概念
引用就是别名,而右值引用也是别名,但是只能引用右值。例如:
int add(int num1,int num2) {
return num1 + num2;
}
int main() {
// 函数按值返回的是一个右值,可以右值引用
int&& num1 = add(2,5);
// 10是一个纯右值,可以右值引用
int&& num2 = 10;
}
引用与右值引用
- 引用只能引用左值不能引用右值
- const引用既可以引用左值又可以引用右值
- 右值引用只能引用右值,一般情况下不能直接引用左值
完美转发
存在这样一种情况:
void Func(int x) {
}
template<typename T>
void PerfectForward(T t) {
Func(t);
}
在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数,这就叫做完美转发。
但是一般情况下,像上边这样写会导致右值丢失原来的右值的属性。
void Fun(int& x) { cout << "lvalue ref" << endl; }
void Fun(int&& x) { cout << "rvalue ref" << endl; }
void Fun(const int& x) { cout << "const lvalue ref" << endl; }
void Fun(const int&& x) { cout << "const rvalue ref" << endl; }
template<typename T>
void PerfectForward(T&& t) { Fun((t)); }
int main()
{
PerfectForward(10); // rvalue ref
int a;
PerfectForward(a); // lvalue ref
PerfectForward(std::move(a)); // rvalue ref
const int b = 8;
PerfectForward(b); // const lvalue ref
PerfectForward(std::move(b)); // const rvalue ref
return 0;
}
像上边的代码,本来期待的结果都写在调用的函数的后面,但是实际上代码运行的结果却……
所有函数参数都变成了左值。
为了解决这个问题达到完美转发,使用std::forward();函数
void PerfectForward(T&& t) { (Fun(forward(t))); }
结果如下:
lambda表达式
语法规则
lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement }
- lambda表达式各部分说明
[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。 - (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略
- mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
- ->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
- {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。
关于捕捉列表:
捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用。
- [var]:表示值传递方式捕捉变量var
- [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
- [&var]:表示引用传递捕捉变量var
- [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
- [this]:表示值传递方式捕捉当前的this指针
- 捕捉列表可以有多个参数,中间以逗号间隔
举个栗子:
int main() {
int a = 10;
int b = 20;
auto fun = [=](int a,int b) {return a + b; };
cout << fun(10,10);
}
函数对象
函数对象又可以成为仿函数。就是在类里边重载operator()运算符的类对象。
举个栗子:
class Test {
public:
Test()
:_x(0)
{};
int operator()(int num1,int num2,int num3) {
return num1 * num2 * num3;
}
private:
int _x;
};
int main() {
Test t;
// 仿函数
t(10, 20, 1);
}