一.lambda
1.本质:lambda对象是⼀个匿名函数对象,它可以定义在函数内部。
注:lambda表达式语法使⽤层⽽⾔没有类型,所以我们⼀般是⽤auto或者模板参数定义的对象去接收lambda对象。
2.表达式:[capture-list] (parameters)-> return type { function boby }
捕捉列表:
<1>该列表出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数
<2>捕捉列表能够捕捉上下⽂中的变量供lambda函数使⽤,捕捉列表可以传值捕捉和传引⽤捕捉
<3>捕捉列表哪怕为空也不能省略。
参数列表:
<1>与普通函数的参数列表功能类似
<2>如果不需要参数传递,则可以连同()⼀起省略
返回值:
<1>⽤追踪的返回类型形式来声明函数的返回值类型
<2>没有返回值时,此部分可省略;⼀般返回值类型明确情况下,也可省略,由编译器对返回类型进⾏推导。
函数体:
<1>函数体的实现跟普通函数完全类似,在该函数体内,除了可以使⽤其参数外,还可以使⽤所有捕获到的变量
<2>函数体为空也不能省略。
3.捕捉列表:lambda的捕捉列表本质是生成的仿函数类的成员变量。lambda表达式中默认只能⽤lambda函数体和参数中的变量,如果想⽤外层作⽤域中的变量就需要进⾏捕捉,捕捉方式有三种:
<1>捕捉列表中显示的传值捕捉和传引⽤捕捉,捕捉的多个变量⽤逗号分割。例如:[x,y,&z]表示x和y值捕捉,z引⽤捕捉。
注1:传值捕捉时,该值相当于被const修饰,不可以在函数体内被更改,此时可将mutable加在参数列表的后⾯取消其常量性,即使⽤该修饰符后,传值捕捉的对象就可以被修改了,但是修改还是形参对象,不会影响实参。注意使⽤该修饰符后,参数列表不可省略(即使参数为空)。
注2:传引用捕捉时,可以在函数体内修改该值
<2>在捕捉列表中隐式捕捉,我们在捕捉列表写⼀个=表示隐式值捕捉,在捕捉列表写⼀个&表示隐式引⽤捕捉,这样lambda表达式中⽤了哪些变量,编译器就会⾃动捕捉那些变量。
<3>在捕捉列表中混合使⽤隐式捕捉和显示捕捉。例如:[=,&x]表⽰其他变量隐式值捕捉,x引⽤捕捉;[&,x,y]表示其他变量引⽤捕捉,x和y值捕捉。
注:当使⽤混合捕捉时,第⼀个元素必须是&或=,并且&混合捕捉时,后⾯的捕捉变量必须是值捕捉,同理=混合捕捉时,后⾯的捕捉变量必须是引⽤捕捉。
4.原理:lambda底层是仿函数对象。即写了⼀个lambda以后,编译器会⽣成⼀个对应的仿函数的类。仿函数的类名是编译按⼀定规则⽣成的,保证不同的lambda⽣成的类名不同,lambda参数/返回类型/函数体就是仿函数operator()的参数/返回类型/函数体,lambda的捕捉列表本质是⽣成的仿函数类的成员变量,即捕捉列表的变量都是lambda类构造函数的实参,若为隐式捕捉,编译器要看使⽤哪些就传哪些对象。
5.lambda表达式如果在函数局部域中,它可以捕捉lambda位置之前定义的变量,但是不能捕捉静态局部变量和全局变量,静态局部变量和全局变量也不需要捕捉,lambda表达式中可以直接⽤。
注:lambda表达式如果定义在全局位置,捕捉列表必须为空。
6.使用场景:使⽤lambda定义可调⽤对象;线程中定义线程的执⾏函数逻辑;智能指针中定
制删除器……
二.新的类功能
1.默认的移动构造和移动赋值
回顾:原来C++类中,有6个默认成员函数:构造函数;析构函数;拷⻉构造函数;拷⻉赋值重载;取地址重载;const取地址重载,最后重要的是前4个
<1>C++11新增了两个默认成员函数,移动构造函数和移动赋值运算符重载。
<2>移动构造:如果没有自定义实现移动构造函数,且没有实现析构函数、拷⻉构造、拷⻉赋值重载中的任意⼀个,那么编译器会⾃动⽣成⼀个默认移动构造。
默认⽣成的移动构造函数对于内置类型:逐成员按字节拷⻉
默认⽣成的移动构造函数对于自定义类型:如果这个成员需要实现移动构造,则调⽤移动构造;若是不需要实现移动构造,调⽤拷⻉构造。
<3>移动赋值:如果没有自定义实现移动赋值重载函数,且没有实现析构函数、拷⻉构造、拷⻉赋值重载中的任意⼀个,那么编译器会⾃动⽣成⼀个默认移动赋值重载函数。
默认⽣成的移动赋值重载函数对于内置类型:逐成员按字节拷⻉
默认⽣成的移动赋值重载函数对于自定义类型:如果这个成员需要实现移动赋值,则调⽤移动赋值;若是不需要实现移动赋值,调⽤拷贝赋值。
<4>如果自定义了移动构造或者移动赋值,那么编译器不会⾃动提供拷⻉构造和拷⻉赋值。
2.成员变量声明时给缺省值:成员变量声明时给缺省值是给初始化列表使⽤的,如果没有显示在初始化列表初始化,那么初始化列表就⽤这个缺省值初始化
3.default和delete
控制要使用的默认函数:default关键字显示指定移动构造⽣成。
限制默认函数的生成:
(1)在C++98中,将该函数设置成private,并且只声明不调用
(2)在C++11中,在该函数声明后加上=delete即可,该语法指示编译器不⽣成对应函数的默认版本,故称 =delete 修饰的函数为删除函数。
4.final和override
final:用于修饰类,被final修饰的类不可以被继承
override:用于修饰派生类,被override修饰的派生类不可以被重写
5.STL中的变化
C++11在STL中新增的内容中,新容器:unordered_map,unordered_set和新接口:emplacement对于提高效率有很大帮助
三.包装器
注:function,bind被定义<functional>头⽂件
1.function
<1>本质:std::function是⼀个类模板,也是⼀个包装器。
<2>作用:std::function的实例对象可以包装存储其他的可以调⽤对象,包括函数指针、仿函数、lambda、bind表达式等,存储的可调⽤对象被称为std::function的⽬标。
注:若std::function不含⽬标,则称它为空。调⽤空std::function的⽬标导致抛出std::bad_function_call异常。
<3>function原型:
template <class T>
class function; template <class Ret, class... Args>
class function<Ret(Args...)>;
<4>优势:统⼀不同对象的类型,对它们都可以进⾏包装,这样在很多地⽅就⽅便声明可调⽤对象的类型
2.bind
<1>bind是⼀个函数模板,它也是⼀个可调⽤对象的包装器,可以把他看做⼀个函数适配器,对接收的fn可调⽤对象进⾏处理后返回⼀个可调⽤对象。
<2>作用:bind可以⽤来调整参数个数和参数顺序。
注:bind调整参数个数的实际用途不大,但是改变参数顺序的实际意义很大
<3>格式:auto newCallable = bind(callable,arg_list);
解释:当调⽤newCallable时,newCallable会调⽤callable,并传给它arg_list中的参数。
newCallable本⾝是⼀个可调⽤对象;
arg_list是⼀个逗号分隔的参数列表,对应给定的callable的参数。arg_list中的参数可能包含形如_n的名字,其中n是⼀个整数,这些参数是占位符,表⽰newCallable的参数,它们占据了传递给newCallable的参数的位置。数值n表⽰⽣成的可调⽤对象中参数的位置,例如:_1为newCallable的第⼀个参数,_2为第⼆个参数,以此类推。_1/_2/_3....这些占位符放到placeholders的⼀个命名空间中。
注:通过改变arg_list中占位符的位置,可以实现改变参数的顺序;也可以通过在某位置指定具体值,利用占位符确定传入参数的位置,从而实现某个变量的绑定