在C++中lambda-expression
的结果叫做闭包对象(closure object)
。本篇文章并非是介绍C++ lambda的用法的(这一点《TC++PL》、《C++ Primer》中都十分详细,或者看我之前的总结C++11的语法糖#lambda表达式),而是从LLVM-IR来分析在Clang中是如何实现lambda-expression
的。
C++标准中是这么描述lambda的:
**[ISO/IEC 14882:2014 §5.1.2.2]**The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the closure object.
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type — whose properties are described below. This class type is neither an aggregate (8.5.1) nor a literal type (3.9).
A closure object behaves like a function object (20.9).
标准中提到了闭包类型是一个独一无二的非union类类型。来看一下Clang中对于lambda的实现:
1 | int main(){ |
上面的代码是一个lambda对象捕获了一个int类型与double类型对象并且接受一个int型参数,来看下其LLVM-IR代码:
1 | %class.anon = type { i32*, double* } |
这里有几个需要重点关注的部分:
1 | %class.anon = type { i32*, double* } |
可以看到LLVM中lambda的实现就是一个重载了operator()
的匿名类类型对象,其中的捕获到的参数都作为了这个类的数据成员,而调用接收的参数是该operator()
所接收的参数。
下面看一下我手写一个与上面lambda表达式实现相同功能的函数对象(重载了operator()
的类):
1 | class A{ |
其LLVM-IR代码为:
1 | %class.A = type { i32*, double* } |
来对比下我上面提到的三个关键部分:
1 |
|
可以看到,我们手写的函数对象与lambda表达式由编译器生成的一模一样…
被捕获列表捕获的对象是作为该编译器生成类的数据成员存放的,而接收参数是作为operator()
的参数获得的,上面IR代码中lambda与我手写的最大的区别是,lambda并不会生成相应的构造函数。
由上可知,所以在Clang中,lambda就是以函数对象的方式实现的…
但是此时又可以延伸出一个问题:既然lambda是函数对象,那么我能在lambda的函数体中获取this指针吗(lambda的this)?
答案是否定的。一个例子:
1 | class A |
尝试编译没有捕获却在lambda函数体中使用this的代码会有下列编译错误:
1 | error: 'this' cannot be implicitly captured in this context |
Lambda的捕获列表是可以捕获this的:
1 | class A |
另外,有了lambda
再组合STL中的<functional>
,简直超强屠龙技啊。