一、相关说明
- 首先这篇博客并不是讲lambda表达式的语法,而是对其背后运作原理的深度剖析。
- 需要读者具备c++类的基础知识。
- 只要坚持看完这篇博文,我保证你或多或少肯定会有所收获。
- 欢迎大家在评论区发表自己的见解,来一场思想的碰撞。
二、问题引入
- 首先不要着急运行下面的程序,来猜猜这段程序的输出结果将会是什么?并将你猜测的结果发表在评论区。
// lambdaDemo.cpp
#include <iostream>
using namespace std;
int main()
{
uint32_t numRef = 10, numVal = 20;
auto func = [&numRef, numVal](string str) mutable {
++numVal;
cout << numRef << " " << numVal << " " << str << endl;
++numRef;
};
++numRef;
++numVal;
func("hello");
func("world");
cout << numRef << " " << numVal << endl;
return 0;
}
- 猜完了,使用
g++ lambdaDemo.cpp -std=c++17 -Wall -o lambdaDemo
编译并运行下代码,看看结果。 - 如果你猜对了,ok。这篇文章对你来说可能意义不是很大。没猜对?强烈建议通读本文。
三、原理解析
1、思考
- 编译器会将lambda表达式解释为一个类,并且这个类重载了
()
运算符,这个()
就是我们代码中mutable关键字前面的那个。 - 既然是一个类,那么大家根据运行结果猜一下,在我们代码中,该类 有哪些私有数据成员? 有哪些成员函数? 什么时候创建对象?。你愿意的话,也可以将猜测结果发在评论区。
2、原理揭晓
- ok,下面我们揭晓答案,请保证你已经思考过上面的问题了,因为这样更有助于你记住下面的结论。
- 编译器会将
[]
中捕获的变量,作为类的私有数据成员。并且如果是按引用捕获,那么私有数据成员也是引用类型,按值捕获,私有数据成员就是一般变量。并且该类会生成一个构造函数,其参数就是我们捕获的变量。结果如下:
class __lambda_7_17
{
private:
uint32_t &numRef;
uint32_t numVal;
public:
__lambda_7_17(uint32_t &_numRef, uint32_t &_numVal)
: numRef{_numRef}, numVal{_numVal}
{
}
};
- 编译器会生成一个重载
()
的成员函数,这个函数的函数体,就是我们lambda表达式的函数体。如下:
class __lambda_7_17
{
public:
inline void operator()(std::basic_string<char> str)
{
++numVal;
cout << numRef << " " << numVal << " " << str << endl;
++numRef;
}
- 当这个lambda表达式被定义之后,就会马上创建该类的对象。注意并不是在调用该lambda时才创建对象。
- 最后,这是我们上面的代码被翻译成类之后的结果,使用的工具cppinsights:
#include <iostream>
using namespace std;
int main()
{
uint32_t numRef = 10;
uint32_t numVal = 20;
class __lambda_7_17
{
public:
inline void operator()(std::basic_string<char> str)
{
++numVal;
std::operator<<(std::operator<<(std::operator<<(std::cout.operator<<(numRef), " ").operator<<(numVal), " "), str).operator<<(std::endl);
++numRef;
}
private:
uint32_t &numRef;
uint32_t numVal;
public:
__lambda_7_17(uint32_t &_numRef, uint32_t &_numVal)
: numRef{_numRef}, numVal{_numVal}
{
}
};
__lambda_7_17 func = __lambda_7_17{numRef, numVal};
++numRef;
++numVal;
func.operator()(std::basic_string<char>("hello", std::allocator<char>()));
func.operator()(std::basic_string<char>("world", std::allocator<char>()));
std::operator<<(std::cout.operator<<(numRef), " ").operator<<(numVal).operator<<(std::endl);
return 0;
}
四、进一步思考
- 代码的运行结果我就不贴在这里了,相信大家都已经知道了,还没知道的同学赶紧一键三连,有空在回头看看。
- 再留一些问题,引导大家进一步思考,加深对lambda的理解。
- 为什么明明numRef和numVal被++的次数是一样的,而最后打印出的numRef被加了3,而numVal只加了1?
- 如果我把第14行的++numRef改为numRef = 0,最终结果会变成啥?
- 记得把问题答案贴在评论区哦!
觉得不错请点赞👍