C++中lambda的应用场景及编译器实现原理

一、使用场景

1、局部函数的需求场景及限制

在标准C++(C++11之前)中,是没有局部函数这种语法的。但是在有些场景下,使用局部函数可以极大的简化代码,比方说,输入参数是一个圆(由圆心和半径表示),判断给定的两个点,是否一个在圆外部,一个在内部,此时最好有一个判断一个点是否在圆形内部的函数接口,然后分别判断两个点。
struct Point
{
int x;
int y;
};
bool IsTrue(Point &rstCenter, int iRadius, Point &P1, Point &P2)
{
bool PointInCircle(Point &rstPoint)
{
return (rstPoint.x - rstCenter.x) * (rstPoint.x - rstCenter.x) + (rstPoint.y - rstCenter.y) * (rstPoint.y - rstCenter.y) <= iRadius * iRadius;
}
return PointInCircle(P1) != PointInCircle(P2);
}

但是,由于C++不允许使用局部函数,所以上面的代码并不能编译通过。在lambda函数引入之前,通常的变通方法是定义局部类型,并使用静态函数来作为局部函数,但是限制是不能使用所在函数的局部变量。
tsecer@harry: cat -n localstruct.cpp
1 struct Point
2 {
3 int x;
4 int y;
5 };
6 bool IsTrue(Point &rstCenter, int iRadius, Point &P1, Point &P2)
7 {
8 struct localstruct
9 {
10 static bool PointInCircle(Point &rstPoint)
11 {
12 return (rstPoint.x - rstCenter.x) * (rstPoint.x - rstCenter.x) + (rstPoint.y - rstCenter.y) * (rstPoint.y - rstCenter.y) <= iRadius * iRadius;
13 }
14 };
15 return localstruct::PointInCircle(P1) != localstruct::PointInCircle(P2);
16 }
tsecer@harry: g++ -c localstruct.cpp
localstruct.cpp: 在静态成员函数‘static bool IsTrue(Point&, int, Point&, Point&)::localstruct::PointInCircle(Point&)’中:
localstruct.cpp:12:25: 错误:use of parameter from containing function
return (rstPoint.x - rstCenter.x) * (rstPoint.x - rstCenter.x) + (rstPoint.y - rstCenter.y) * (rstPoint.y - rstCenter.y) <= iRadius * iRadius;
^
localstruct.cpp:6:6: 错误:‘Point& rstCenter’已在此声明过
bool IsTrue(Point &rstCenter, int iRadius, Point &P1, Point &P2)
^
localstruct.cpp:12:54: 错误:use of parameter from containing function
return (rstPoint.x - rstCenter.x) * (rstPoint.x - rstCenter.x) + (rstPoint.y - rstCenter.y) * (rstPoint.y - rstCenter.y) <= iRadius * iRadius;
^
localstruct.cpp:6:6: 错误:‘Point& rstCenter’已在此声明过
bool IsTrue(Point &rstCenter, int iRadius, Point &P1, Point &P2)
^
localstruct.cpp:12:83: 错误:use of parameter from containing function
return (rstPoint.x - rstCenter.x) * (rstPoint.x - rstCenter.x) + (rstPoint.y - rstCenter.y) * (rstPoint.y - rstCenter.y) <= iRadius * iRadius;
^
localstruct.cpp:6:6: 错误:‘Point& rstCenter’已在此声明过
bool IsTrue(Point &rstCenter, int iRadius, Point &P1, Point &P2)
^
localstruct.cpp:12:112: 错误:use of parameter from containing function
return (rstPoint.x - rstCenter.x) * (rstPoint.x - rstCenter.x) + (rstPoint.y - rstCenter.y) * (rstPoint.y - rstCenter.y) <= iRadius * iRadius;
^
localstruct.cpp:6:6: 错误:‘Point& rstCenter’已在此声明过
bool IsTrue(Point &rstCenter, int iRadius, Point &P1, Point &P2)
^
localstruct.cpp:12:128: 错误:use of parameter from containing function
return (rstPoint.x - rstCenter.x) * (rstPoint.x - rstCenter.x) + (rstPoint.y - rstCenter.y) * (rstPoint.y - rstCenter.y) <= iRadius * iRadius;
^
localstruct.cpp:6:6: 错误:‘int iRadius’已在此声明过
bool IsTrue(Point &rstCenter, int iRadius, Point &P1, Point &P2)
^
localstruct.cpp:12:138: 错误:use of parameter from containing function
return (rstPoint.x - rstCenter.x) * (rstPoint.x - rstCenter.x) + (rstPoint.y - rstCenter.y) * (rstPoint.y - rstCenter.y) <= iRadius * iRadius;
^
localstruct.cpp:6:6: 错误:‘int iRadius’已在此声明过
bool IsTrue(Point &rstCenter, int iRadius, Point &P1, Point &P2)
^
tsecer@harry:

 

2、lambda函数的解决方法

tsecer@harry: cat -n locallambda.cpp
1 struct Point
2 {
3 int x;
4 int y;
5 };
6
7 bool IsTrue(Point &rstCenter, int iRadius, Point &P1, Point &P2)
8 {
9 auto PointInCircle = [&](Point &rstPoint) -> bool
10 {
11 return (rstPoint.x - rstCenter.x) * (rstPoint.x - rstCenter.x) + (rstPoint.y - rstCenter.y) * (rstPoint.y - rstCenter.y) <= iRadius * iRadius;
12 };
13 return PointInCircle(P1) != PointInCircle(P2);
14 }
tsecer@harry: g++ -c -std=c++11 locallambda.cpp
tsecer@harry:
tsecer@harry:

二、实现原理

大致来说,gcc对于lambda函数的实现,相当于是允许通过捕捉语句声明引用哪些所在函数的局部变量,把这些捕捉变量放在一个结构中,并在结构中定义一个函数操作符。这些看起来和自己实现比较类似,但是区别在于它添加了语法,允许明确声明的局部变量引用,而且会帮助lambda函数自动计算引用了哪些所在函数的局部变量,编译时生成对应的类型和操作符。
tsecer@harry: cat -n autofunc.cpp
1 #include <functional>
2
3 typedef std::function<bool (int, int)> stdFunc;
4
5 int main(int argc, const char * argv[])
6 {
7 int a, b, c, d;
8 auto ff = [&](int x, int y) -> bool
9 {
10 return x + y + a + d;
11 };
12
13 ff(0x1111, 0x2222);
14
15 stdFunc stdff = ff;
16 stdff(0x3333, 0x4444);
17 }
tsecer@harry: g++ -g -std=c++11 autofunc.cpp
tsecer@harry: gdb ./a.out
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/harry/study/autofunc/a.out...done.
(gdb) b main
Breakpoint 1 at 0x400874: file autofunc.cpp, line 11.
(gdb) r
Starting program: /home/harry/study/autofunc/./a.out

Breakpoint 1, main (argc=1, argv=0x7fffffffe578) at autofunc.cpp:11
11 };
Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.tl2.3.x86_64 libgcc-4.8.5-4.el7.x86_64 libstdc++-4.8.5-4.el7.x86_64
(gdb) ptype ff
type = struct __lambda0 {
int &__a;
int &__d;
}
(gdb) s
13 ff(0x1111, 0x2222);
(gdb)
__lambda0::operator() (__closure=0x7fffffffe460, x=4369, y=8738) at autofunc.cpp:10
10 return x + y + a + d;
(gdb) ptype __closure
type = const struct __lambda0 {
int &__a;
int &__d;
} * const
(gdb)

可以看到,由于lambda函数ff引用了局部变量a、b,编译器在编译时动态生成了这样一个结构
struct __lambda0 {
int &__a;
int &__d;
bool operator()(int x, int y)
{
return x + y+ __a + __b;
}
}
反汇编中的__closure看作结构的this指针也可以。

三、和std库中functional的交互

知道它的实现原理之后,它就可以和std::function一起使用了,并且和定义了函数调用操作符的结构使用方法相同:
tsecer@harry: cat -n lambdabind.cpp
1 #include <functional>
2
3 typedef std::function<int (int, int)> stdFunc;
4
5 int main(int argc, const char * argv[])
6 {
7 auto ff = [&](int x) -> int
8 {
9 return x + argc;
10 };
11 stdFunc stdff = std::bind(ff, std::placeholders::_1);
12 return stdff(11, 22);
13 }
14
tsecer@harry: g++ -g -std=c++11 lambdabind.cpp
tsecer@harry: ./a.out
tsecer@harry: echo $?
12

posted on 2020-01-06 18:47  tsecer  阅读(1444)  评论(0编辑  收藏  举报

导航