C++拾遗--lambda表达式原理

C++拾遗--lambda表达式原理

前言

lambda表达式是在C++11新标准中提出的。在lambda表达式中,我们集中梳理了一下它的使用。现在来讨论下它的实现原理。

正文

1.函数对象

类的对象跟括号结合,表现出函数一般的行为,这个对象可以称作是函数对象。

#include <iostream>
using namespace std;
class MyClass
{
public:
	//重载函数调用运算符()
	int operator()(int i)
	{
		return i;
	}
};
int main()
{
	MyClass my;
	//my()的调用行为似同函数
	int i = my(1);     //本质是调用 my.operator()(1)
	cout << "i = " << i << endl;
	cin.get();
	return 0;
}

运行

这个示例说明函数对象的本质是重载了函数调用运算符。当一个类重载了函数调用运算符()后,它的对象就成了函数对象。这是理解lambda表达式内部实现的基础。

2.lambda表达式原理

原理:编译器会把一个lambda表达式生成一个匿名类的匿名对象,并在类中重载函数调用运算符。

我们从最简单的lambda表达式入手,从易到难

2.1 无捕获列表和参数列表

auto print = []{cout << "zhangxiang" << endl; };

编译器会把这一句翻译成如下情形:

//用给定的lambda表达式生成相应的类
class print_class
{
public:
	void operator()(void) const
	{
		cout << "zhangxiang" << endl;
	}
};
//用构造的类创建对象,print此时就是一个函数对象
auto print = print_class();

生成类的类名命名规则可以多变,不一定非得这样。

2.2 无捕获列表但有参数列表

auto add = [](int a, int b){return a + b; };

编译器会把这一句翻译成如下情形:

class add_class
{
public:
	auto operator()(int a, int b) const
	{
		return a + b;
	}
};
auto add = add_class();

2.3 有捕获列表,参数列表可选

由于捕获方式分为两种:引用捕获、值捕获,故此种情况下,又可细分。

2.3.1 值捕获

int year = 19900212;
char *name = "zhangxiang";
//采用值捕获,捕获所有的已定义的局部变量,如year,name
auto print = [=](){
	cout << year << ends << name << endl;
};

翻译

int year = 19900212;
char *name = "zhangxiang";
class print_class
{
public:
	//根据捕获列表来决定构造函数的参数列表形式
	print_class(int year, char *name) :year(year), name(name)
	{

	}
	void operator()(void) const
	{
		cout << year << ends << name << endl;
	}
private:
	int year;
	char *name;
};
auto print = print_class(a, str);

运行效果是一样的,就不演示了。

2.3.2 引用捕获

int year = 19900212;
char *name = "zhangxiang";
auto print = [&](){
	year++;
	cout << year << ends << name << endl;
};

翻译

int year = 19900212;
char *name = "zhangxiang";
class print_class
{
public:
	//由于是引用捕获,参数列表采用引用的方式
	print_class(int &year, char *&name) :year(year), name(name)
	{

	}
	void operator()(void) const
	{
		year++;   //编译通过,const对引用类型无效
		cout << year << ends << name << endl;
	}
private:
	int &year;
	char *&name;
};

经过测试,效果也是一样的。

2.3.3 混合捕获

int year = 19900212;
int shoes = 42;
char *name = "zhangxiang";
auto show = [&, shoes]()mutable{
	shoes++;
	year++;
	cout << year << ends << shoes << ends << name << endl;
};

翻译

int year = 19900212;
int shoes = 42;
char *name = "zhangxiang";
class show_class
{
private:
	int &year;
	mutable int shoes;
	char *&name;
public:
	show_class(int &year, int shoes, char *&name) :year(year), shoes(shoes), name(name)
	{

	}
	void operator()(void)const
	{
		shoes++;
		year++;
		cout << year << ends << shoes << ends << name << endl;
	}
};
auto show = show_class(year, shoes, name);
show();

默认情况下,经过值捕获的变量是不可以被修改的,除非在参数列表后加关键字mutable。以上代码展示了,对应类中mutable是如何加的。

总结

以上这些示例代码基本上把各种情形的lambda表达式的实现原理都展现出来了,仔细想想还是挺容易的。

本专栏目录

所有内容的目录

时间: 2024-11-06 03:43:39

C++拾遗--lambda表达式原理的相关文章

C++拾遗--lambda表达式

C++拾遗--lambda表达式 前言 有时,我们需要在函数内部频繁地使用某一功能.此时,我们可以把这种功能写成一个独立的函数.而实际上,这个新的函数很可能是不需要在其它的地方进行调用的.我们想限定它的作用范围,最好是仅限于当前函数.而函数的内部是不可以重新定义其它的函数的.为了解决这个问题,在新的标准中,C++引入了lambda表达式(lambda expression)的概念.有了lambda表达式,C++向一门完美的语言又进了一大步.总的来说,lambda表达式极大地提升了C++的函数运用

JDK8 的 Lambda 表达式原理

JDK8 使用一行 Lambda 表达式可以代替先前用匿名类五六行代码所做的事情,那么它是怎么实现的呢?从所周知,匿名类会在编译的时候生成与宿主类带上 $1, $2 的类文件,如写在 TestLambda 中的匿名类产生成类文件是 TestLambda$1.class, TestLambda$2.class 等. 我试验了一下,如果使用的是 Lambda 表达式并不会生成额外的类文件,那么字节码里是什么样子的?来看下用  javap -c 反编译出下面文件产生的 TestLambda.class

Java 8 动态类型语言Lambda表达式实现原理分析

Java 8支持动态语言,看到了很酷的Lambda表达式,对一直以静态类型语言自居的Java,让人看到了Java虚拟机可以支持动态语言的目标. import java.util.function.Consumer; public class Lambda { public static void main(String[] args) { Consumer<String> c = s -> System.out.println(s); c.accept("hello lambd

Java Lambda表达式 实现原理分析

https://blog.csdn.net/qq_37960603/article/details/85028867 在类编译时,会生成一个私有静态方法+一个内部类. 在内部类中实现了函数式接口,在实现接口的方法中,会调用编译器生成的静态方法. 在使用lambda表达式的地方,通过传递内部类实例,来调用函数式接口方法. 原文地址:https://www.cnblogs.com/eryun/p/12149841.html

.NET中那些所谓的新语法之三:系统预定义委托与Lambda表达式

开篇:在上一篇中,我们了解了匿名类.匿名方法与扩展方法等所谓的新语法,这一篇我们继续征程,看看系统预定义委托(Action/Func/Predicate)和超爱的Lambda表达式.为了方便码农们,.Net基类库针对实际开发中最常用的情形提供了几个预定义好的委托,这些委托可以直接使用,无需再重头定义一个自己的委托类型.预定义委托在.Net基类库中使用的比较广泛,比如在Lambda表达式和并行计算中都大量地使用,需要我们予以关注起来! /* 新语法索引 */ 1.自动属性 Auto-Impleme

C#中的Lambda表达式和表达式树

在C# 2.0中,通过方法组转换和匿名方法,使委托的实现得到了极大的简化.但是,匿名方法仍然有些臃肿,而且当代码中充满了匿名方法的时候,可读性可能就会受到影响.C# 3.0中出现的Lambda表达式在不牺牲可读性的前提下,进一步简化了委托. LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态.这些操作表示了各种关于数据的逻辑,例如数据筛选,数据排序等等.通常这些操作都是用委托来表示.Lambda表达式是对LINQ数据操作的一种符合语言习惯的表示方式. Lambda表达式不仅可以用来创

Python之lambda表达式和内置函数

lambda表达式其实就是简化的函数表达式. 它只用于处理简单逻辑, 它会自动return数据 通常定义一个函数,按照以下形式: def  func(arg):       return arg +1 result = func(100) print result 101 以上函数用lambda表达式可以这么写: func2 = lambda a: a+1 result = func2(100) print result 在lambda表达式中,func2 相当于函数表达式中的func,即函数的

spark2.x由浅入深深到底系列六之RDD 支持java8 lambda表达式

学习spark任何技术之前,请正确理解spark,可以参考:正确理解spark 我们在 http://7639240.blog.51cto.com/7629240/1966131 中已经知道了,一个scala函数其实就是java中的一个接口,对于java8 lambda而言,也是一样,一个lambda表达式就是java中的一个接口.接下来我们先看看spark中最简单的wordcount这个例子,分别用java8的非lambda以及lambda来实现: 一.非lambda实现的java spark

1.2 lambda 表达式的语法

1.2 lambda 表达式的语法 还以上一节中的排序为例.我们传递代码来检查某个字符串的长度是否小于另一个字符串的长度,如下所示: Integer.compare(first.length(), second.length()) first 和second 是什么呢?它们都是字符串.Java 是一个强类型的语言,因此我们必须同时指定类型,如下: (String first, String second) -> Integer.compare(first.length(), second.len