为了解决冗杂的函数指针(void (*p)(int a)),仿函数(通过类内重载operator())。C++11定义了lambda表达式,作为一个匿名函数对象,它可以直接再函数内部直接定义使用。以及如何像printf一样一直传入参数的问题
一.lambda表达式
书写格式
【capture-list】(parameters)mutable-> return-type {statement}
-
capture-list:捕捉列表,捕捉上下文中的变量供lambda函数使用
- 【var】:表示值传递捕捉变量var(const变量不可修改)
- 【=】:表示值传递方式捕捉所有父作用域中的变量(含this)
- 【&var】:表示引用传递捕捉变量var
- 【&】:表示引用传递捕捉所有父作用域的变量(含this)
-
parameters:参数列表,一致于普通函数的参数列表,若不需参数传递,可以省略()
-
mutable:默认情况下,lambda函数是const函数,使用该关键字取消常量性
-
->returntype:返回值类型明确情况下,可以省略,由编译器对返回类型推导
-
{statement}:函数体
注意:语法上捕捉列表可由多个捕捉项构成,以逗号分隔。如【&a, &b】,【&,this】(this通过传值方式捕捉)。捕捉列表不允许变量重复传递,lambda表达式之间不能相互赋值
情景1:

情景2:传值捕捉,局部域的修改不影响域外的a, b。栈帧销毁

情景3:使用mutable取消常量性

小结:lambda底层是仿函数,编译器生成仿函数的类,传值传参就是在给仿函数传递,又通过调用构造函数和operator()完成任务。使用UUID作为lambda唯一标识码



可变参数模板
怎么像printf一样一直传入参数?
可变参数模板:创建接收可变参数的函数模板和类模板
- 我们可以观察到,要使用这个模板需要将参数包扩展
- 解参数包1
数组初始化时会推断应该开多大的数组,这时会把扩展包展开,调用函数

- 解参数包2

使用场景
cpp
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date构造" << endl;
}
Date(const Date& d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{
cout << "Date拷贝构造" << endl;
}
private:
int _year;
int _month;
int _day;
};
template<class ...Args>
Date* Creat(Args ...args)
{
Date* ret = new Date(args...);
return ret;
}
int main()
{
Date* p1 = Creat();
Date* p2 = Creat(2023);
Date* p3 = Creat(2023, 9);
Date* p4 = Creat(2023, 9, 27);
Date d(2023, 1, 1);
Date* p5 = Creat(d);
}
emplace_back模板的可变参数+万能引用

- 使用场景
emplace_back通过参数包使用直接构造,push_back使用构造函数出对象来,再作为将亡值,调用移动构造。对于移动构造的消耗不是很大,于是两种方法效率差不多

- 使用情景


移动构造函数&&移动赋值重载
- 如果没有实现移动构造函数,没有实现析构函数,拷贝构造函数,拷贝赋值重载中的任意一个。编译器会自动生成一个默认移动构造,对于内置类型成员(逐字节拷贝)。对于自定义类型成员,如果类内没有实现移动构造,就调用拷贝构造,否则调用调用移动构造函数
- 移动赋值重载同理
- 一般浅拷贝的类不需要自己实现上面两个函数,深拷贝的类需要自己实现
default
- 当实现了析构函数等时,为了使用编译器默认的移动构造,需要使用default关键字

