目录
[1 lambda表达式](#1 lambda表达式)
[2 模板的可变参数](#2 模板的可变参数)
[3 默认成员函数控制](#3 默认成员函数控制)
前言:
继上文介绍了右值概念,本文介绍两个C++11中的重要概念,lambda表达式和模板的可变参数,这两个部分都不算难,重在理解,有了lambda表达式和模板的可变参数的基础才好理解包装器。
1 lambda表达式
常见的,想要排序一个数组,可以利用库里面的sort函数,利用仿函数来排序,排序的方向由仿函数的实现来决定:
cpp
int main()
{
int arr[10] = { 1,4,2,6,9,7,0,8,3,5 };
sort(arr, arr + sizeof(arr) / sizeof(int));
for (auto e : arr)
{
cout << e << " ";
}
return 0;
}
这是普通的排序,那么我们想要实现降序,我们就要写一个仿函数:
cpp
struct Less
{
bool operator()(int a,int b)
{
return a > b;
}
};
int main()
{
int arr[10] = { 1,4,2,6,9,7,0,8,3,5 };
sort(arr, arr + sizeof(arr) / sizeof(int),Less());
for (auto e : arr)
{
cout << e << " ";
}
return 0;
}
但是问题就来了,如果排的是自定义类型呢?如果我们传的是指针但是想要按照指针解引用之后的类型来比较呢?
自定义类型也一样,我们同样可以使用仿函数来解决:
cpp
struct Less
{
bool operator()(const A& a, const A& b)
{
return a._b > b._b;
}
};
int main()
{
vector<A> Aa = { {"c",4},{"a",2}, {"b",3}, {"e",1} };
sort(Aa.begin(), Aa.end(), Less());
return 0;
}
但是问题又来了,如果一个类,有很多很多变量,我们难道就要写这么多个仿函数吗?成员变量一多,能比较的变量一多,代码看起来就有点冗余了,这时候,lambda表达式就出场了:
lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement }
lambda表达式各部分的介绍:
1 capture-list是捕获列表,不可以省略
2 parameters是函数参数列表,没有参数传递就可以省略
3 mutable是一个修饰词,默认情况下lambda表达式是const函数,mutable可以取消const性质,不可以省略
4 return-type是返回值,可以省略
5 statement是函数体,不可以省略
以上是关于lambda表达式的介绍,怎么感觉越看越像函数呢?
对咯,lambda表达式的本质就是仿函数,不信咱们一会儿可以看看汇编。
那么根据lambda表达式的定义,可以分为省略和不可以省略的部分,省略的有参数列表和mutable以及return-type可以省略,其他都不可以省略。
那么用法是什么呢?咱们先来写出来看看:
cpp
int main()
{
[](const A& a1, const A& a2)->bool { return a1._b > a2._b; };
return 0;
}
就看起来还像模像样的,调用却成为了问题,此时,auto就要闪亮登场了,因为它的类型实在过于复杂,我们先用auto来接受,再用typeid打印看看:
cpp
int main()
{
auto Fun = [](const A& a1, const A& a2)->bool { return a1._b > a2._b; };
cout << typeid(Fun).name() << endl;
return 0;
}

这个类型有够吓人的吧?实际上整个表达式都相等,打印出来的结果也都是不一样的:
cpp
int main()
{
auto Fun1 = [](const A& a1, const A& a2)->bool { return a1._b > a2._b; };
auto Fun2 = [] { cout << " " << endl; };
auto Fun3 = [] { cout << " " << endl; };
cout << typeid(Fun1).name() << endl;
cout << typeid(Fun2).name() << endl;
cout << typeid(Fun3).name() << endl;
return 0;
}

这点就有点像符号表修饰的那种感觉,lambda表达式的类型你不知我不知,只有编译器才知道,怎么个知道法呢?在vs2019里面打印出来是lambda后面有很长的字符串,2022的是这样,但是具体怎么修饰的我们不得而知,可以知道的是2019后面的那一长串字符串是uuid:


算法实现,我们就不用深究了。
那么现在了解了lambda表达式,我们怎么使用呢?
如下:
cpp
int main()
{
vector<A> Aa = { {"c",4},{"a",2}, {"b",3}, {"e",1} };
auto Fun = [](A a1, A a2)->bool { return a1._b > a2._b; };
sort(Aa.begin(), Aa.end(), Fun);
return 0;
}
这样可以完成和上面一样的效果,当然,返回值可以不用写,因为编译器会自动推导,那么我们也可以:
cpp
int main()
{
vector<A> Aa = { {"c",4},{"a",2}, {"b",3}, {"e",1} };
auto Fun = [](A a1, A a2)->bool { return a1._b > a2._b; };
sort(Aa.begin(), Aa.end(), Fun);
sort(Aa.begin(), Aa.end(), [](A a1, A a2)
{
return a1._b < a2._b;
}
);
return 0;
}
这样调用也是没有问题的。
那么最基本的用法已经介绍完了,现在来介绍,捕获列表 Mutable 以及底层。
前面提及,捕获列表是不能省略的,那么顾名思义,捕获嘛,捕捉当前作用域的同名的变量:
cpp
int main()
{
int a = 2, b = 1;
auto Fs = [a, b]() { int tmp = a; a = b; b = tmp; };
Fs();
return 0;
}
\]里面捕捉当前局部域里面的同名的变量,捕捉了之后我们是否可以进行交换呢?答案是不可以:

因为这两个a b 不是一样的,并且lambda本身具有const的性质,所以我们需要mutable来改变,但是改变了之后也就没有改变a b的值:
```cpp
int main()
{
int a = 2, b = 1;
auto Fs = [a, b]() mutable{ int tmp = a; a = b; b = tmp; };
Fs();
cout << a << " " << b << endl;
return 0;
}
```
打印出来的结果依然是2 和 1 ,这是因为捕获列表里面实际上是 a b 的拷贝,所以在里面交换了值是不会对外面的 a b起到作用的。
所以呢,想要交换,我们可在参数列表部分使用引用即可,现在了解一下捕获的一些其他用法:
```cpp
int main()
{
int a = 2, b = 1,c = 2,d = 3;
auto Fs1 = [=]()mutable { a++, b++, c++, d++; };
auto Fs2 = [&](){ a++, b++, c++, d++; };
auto Fs3 = [&a]() { a++; };
auto Fs4 = [&, a]() { b++,c++,d++ ; };
return 0;
}
```
这里我们可以总结为:
**1 \[=\]以传值的方式捕获局部域中的所有变量**
**2 \[\&\]以引用的方式捕获局部域中的所有变量**
**3 \[\&,val\]以引用的方式捕获局部域中的所有变量,除了val**
**4 \[\&val\]以引用的方式捕获局部域中的val**
既然是捕获,可以捕获显式的,也可以捕获隐式的,比如this指针,\[this\]代表捕获this指针
也可以进行混合捕获,比如\[= , \&a,\&b\],以引用的方式捕捉a b,其他都以传值的方式捕捉,但是呢,不允许传值捕获两次\[=,a\],这样就出问题了,也即是说不能用两次相同的捕获方式捕获同一变量。
那么现在就简单看一下lambda的底层:


看看咯,底层依旧是仿函数。
*** ** * ** ***
## 2 模板的可变参数
模板的可变参数,在C++11中可以经常看到的:

就比如emplace的参数,就是模板的可变参数,没错,那三个点也算进去了!
其实我们很早很早就看到过了:

有思考过printf为什么可以一次性打印多个参数吗?因为模板的可变参数,在C语言里面可以一次性打印多个值(只用一次printf),那么我们想用C++实现怎么办呢?
首先简单介绍一下模板的可变参数的基本概念:
> // Args是一个模板参数包,args是一个函数形参参数包
>
> // 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
>
> template \