C++11的lambda表达式

这篇具有很好参考价值的文章主要介绍了C++11的lambda表达式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Lambda表达式是一种匿名函数,允许我们在不声明方法的情况下,直接定义函数。它是函数式编程的一种重要特性,常用于简化代码、优化程序结构和增强代码可读性。

lambda表达式的语法非常简单,具体定义如下:

[ captures ] ( params ) specifiers exception -> ret { body }

举例:

#include <iostream>

int main()
{
	int x = 3, y = 4;

	auto res = [x](int y)->int{
		return x + y;
	};

	std::cout << res(y) << std::endl;

	system("pause");
	return 0;
}
  • [ captures ] —— 捕获列表,它可以捕获当前函数作用域的零个或多个变量,变量之间用逗号分隔。在对应的例子中,[x]是一个捕获列表,不过它只捕获了当前函数作用域的一个变量x,在捕获了变量之后,我们可以在lambda表达式函数体内使用这个变量,比如return x * y。另外,捕获列表的捕获方式有两种:按值捕获和引用捕获
  • ( params ) —— 可选参数列表,语法和普通函数的参数列表一样,在不需要参数的时候可以忽略参数列表。对应例子中的(int y)。
  • specifiers —— 可选限定符,C++11中可以用mutable,它允许我们在lambda表达式函数体内改变按值捕获的变量,或者调用非const的成员函数。上面的例子中没有使用说明符。
  • exception —— 可选异常说明符,我们可以使用noexcept来指明lambda是否会抛出异常。对应的例子中没有使用异常说明符。
  • ret —— 可选返回值类型,不同于普通函数,lambda表达式使用返回类型后置的语法来表示返回类型,如果没有返回值(void类型),可以忽略包括->在内的整个部分。另外,我们也可以在有返回值的情况下不指定返回类型,这时编译器会为我们推导出一个返回类型。对应到上面的例子是->int。
  • { body } —— lambda表达式的函数体,这个部分和普通函数的函数体一样。对应例子中的{ return x + y; }。

在lambda函数的定义中,参数列表返回类型都是可选的部分,而捕捉列表函数体都可能为空。那么在极端情况下,C++11中最为简略的lambda函数只需要声明为

  []{};

就可以了。不过理所应当地,该lambda函数不能做任何事情。

捕获列表的作用域

捕获列表中的变量存在于两个作用域——lambda表达式定义的函数作用域以及lambda表达式函数体的作用域。

前者是为了捕获变量,后者是为了使用变量。另外,标准还规定能捕获的变量必须是一个自动存储类型。简单来说就是非静态的局部变量。

C++11的lambda表达式,C++11新特性,c++,开发语言可以看到,当我们把全局变量和一个静态变量放到捕获列表中会报错。

如果我们要在函数体内使用全局变量和静态变量的话直接使用即可。

#include <iostream>

int x = 0;

int main()
{
	static int y = 2;

	auto res = []()->int{
		return x + y;
	};

	std::cout << res() << std::endl;

	system("pause");
	return 0;
}

如果我们将一个lambda表达式定义在全局作用域,那么lambda表达式的捕获列表必须为空。因为根据上面提到的规则,捕获列表的变量必须是一个自动存储类型,但是全局作用域并没有这样的类型,比如:

#include <iostream>

int x = 0;

static int y = 2;

auto res = []()->int {
	return x + y;
};

int main()
{
	std::cout << res() << std::endl;

	system("pause");
	return 0;
}

捕获列表的捕获值和引用

捕获列表的捕获方式分为捕获值和捕获引用。

捕获值

#include <iostream>

int main()
{
	int x = 3, y = 2;

	auto res = [x, y]()->int {
		return x + y;
	};

	std::cout << res() << std::endl;	// 5

	system("pause");
	return 0;
}

捕获引用

捕获引用的语法与捕获值只有一个&的区别,要表达捕获引用我们只需要在捕获变量之前加上&,类似于取变量指针。只不过这里捕获的是引用而不是指针,在lambda表达式内可以直接使用变量名访问变量而不需解引用

#include <iostream>

int main()
{
	int x = 3, y = 2;

	auto res = [&x, &y]()->int {
		return x + y;
	};

	std::cout << res() << std::endl;

	system("pause");
	return 0;
}

如果只是读捕获列表中的值,那么上述两个例子没有区别,但是要修改捕获列表的值就有区别。

void bar1()
{
    int x = 5, y = 8;
    auto foo = [x, y] {
        x += 1;             // 编译失败,无法改变捕获变量的值
        y += 2;             // 编译失败,无法改变捕获变量的值
        return x * y;
    };
    std::cout << foo() << std::endl;
}

void bar2()
{
    int x = 5, y = 8;
    auto foo = [&x, &y] {
        x += 1;
        y += 2;
        return x * y;
    };
    std::cout << foo() << std::endl;
}

在上面的代码中函数bar1无法通过编译,原因是我们无法改变捕获变量的值。这就引出了lambda表达式的一个特性:捕获的变量默认为常量,或者说lambda是一个常量函数(类似于常量成员函数)。

bar2函数里的lambda表达式能够顺利地通过编译,虽然其函数体内也有改变变量x和y的行为。这是因为捕获的变量默认为常量指的是变量本身,当变量按值捕获的时候,变量本身就是值,所以改变值就会发生错误。相反,在捕获引用的情况下,捕获变量实际上是一个引用,我们在函数体内改变的并不是引用本身,而是引用的值,所以并没有被编译器拒绝。

另外,使用mutable说明符可以移除lambda函数的常量性。例如:

void bar1()
{
	int x = 5, y = 8;
	auto foo = [x, y]() mutable{
		x += 1;             
		y += 2;             
		return x * y;
	};
	std::cout << foo() << std::endl;
}

但是这并不意味着二者就没有任何区别了,捕获值和捕获引用最根本上和函数参数的值传递和引用传递是一样的。例如:

void bar1()
{
	int x = 5, y = 8;
	auto foo = [x, y]() mutable{
		x += 1;             // 编译失败,无法改变捕获变量的值
		y += 2;             // 编译失败,无法改变捕获变量的值
		return x * y;
	};

	std::cout << foo() << std::endl;	// 60
	std::cout << x << std::endl;		// 5
	std::cout << y << std::endl;		// 8
}

void bar2()
{
	int x = 5, y = 8;
	auto foo = [&x, &y] {
		x += 1;
		y += 2;
		return x * y;
	};

	std::cout << foo() << std::endl;	// 60
	std::cout << x << std::endl;		// 6
	std::cout << y << std::endl;		// 10
}

对于捕获值的lambda表达式还有一点需要注意,捕获值的变量在定义lambda表达式时就固定下来,无论在定义lambda表达式定义之后如何修改这个变量的值,在lambda函数体内都不会改变。例如:

void bar2()
{
	int x = 5, y = 8;
	auto foo = [x, &y] {
		return x * y;
	};

	x += 1;
	y += 2;

	std::cout << foo() << std::endl;	// 50
	std::cout << x << std::endl;		// 6
	std::cout << y << std::endl;		// 10
}

特殊的捕获方法

lambda表达式的捕获列表除了指定捕获变量之外还有3种特殊的捕获方法。

1.[this] —— 捕获this指针,捕获this指针可以让我们使用this类型的成员变量和函数。

class A
{
public:

	void Print()
	{
		std::cout << "class A" << std::endl;
	}

	void Test()
	{
		auto foo = [this] {
			
			Print();

			x += 2;

			std::cout << x << std::endl;
		};

		foo();
	}

private:

	int x = 2;
};

2.[=] —— 捕获lambda表达式定义作用域的全部变量的值,包括this。

void bar2()
{
	int x = 5, y = 8;
	auto foo = [=] {
		return x * y;
	};

	std::cout << foo() << std::endl;	// 40
}

3.[&] —— 捕获lambda表达式定义作用域的全部变量的引用,包括this。

void bar2()
{
	int x = 5, y = 8;
	auto foo = [&] {
		return x * y;
	};

	std::cout << foo() << std::endl;	// 40
}

无状态的lambda表达式

C++标准对于无状态的lambda表达式有着特殊的照顾,即它可以隐式转换为函数指针,例如:

void f(void(*)()) {}
void g() { f([] {}); } // 编译成功

在上面的代码中,lambda表达式[] {}隐式转换为void(*)()类型的函数指针。同样,看下面的代码:

void f(void(&)()) {}
void g() { f(*[] {}); }

这段代码也可以顺利地通过编译。我们经常会在STL的代码中遇到lambda表达式的这种应用。

在STL中使用lambda表达式

使用lambda表达式作为STL算法的第三个参数

#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
	std::vector<int> x = { 1,2,3,4,5,6 };

	std::cout << *std::find_if(x.begin(), x.end(), [](int i) {

		return (i % 3) == 0;

	}) << std::endl;

	system("pause");
	return 0;
}

这段代码使用了std::find_if算法来搜索满足指定条件的元素,并输出该元素的值。

首先,创建了一个名为x的std::vector,包含了整数1到6的元素。

然后,调用std::find_if算法,传入参数x.begin()和x.end(),用于指定搜索范围为整个容器x。

在这里,使用了Lambda表达式作为第三个参数,对每个元素进行判断。Lambda表达式[](int i) { return (i % 3) == 0; }的作用是判断一个整数是否能被3整除,如果能则返回true,否则返回false。Lambda表达式的参数为当前迭代到的元素,这里表示为i。

std::find_if算法会依次遍历容器中的元素,并将每个元素传入Lambda表达式中进行判断,直到找到第一个满足条件的元素或遍历完整个容器。一旦找到满足条件的元素,std::find_if算法会返回指向该元素的迭代器。

最后,通过*运算符取出迭代器指向的元素值,并使用std::cout输出到标准输出流中。

因此,这段代码输出的结果是容器x中能被3整除的第一个元素的值,即3。

Lambda 表达式具有以下优点:

  1. 简洁:Lambda 表达式可以简洁地定义和使用,省去了传统函数的繁琐定义过程。

  2. 表达力强:Lambda 表达式可以更直观地表达函数的意图,特别适用于一些简单的函数功能。

  3. 更好的可读性:Lambda 表达式的内联定义使得代码更紧凑,更易于理解和维护。

  4. 捕获外部变量:Lambda 表达式可以捕获外部作用域的变量,使得在函数体内可以访问这些变量,方便在函数内部进行计算和操作。

然而,Lambda 表达式也有一些缺点:

  1. 可读性可能有限:过于复杂或嵌套的 Lambda 表达式可能会降低代码的可读性,特别是当 Lambda 表达式较长时。

  2. 不易重用:Lambda 表达式通常用于一次性的函数功能,不太适用于需要重复使用的情况。对于需要在多个地方使用的函数,使用具名的函数对象或函数指针更具可重用性。

  3. 难以调试:由于是匿名函数,Lambda 表达式的调试可能相对困难,特别是在复杂的代码场景中。

综上所述,Lambda 表达式在简洁、表达力等方面具有一些优点,但也需谨慎使用,根据具体情况来判断是否适合使用 Lambda 表达式。文章来源地址https://www.toymoban.com/news/detail-773909.html

到了这里,关于C++11的lambda表达式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java 8 新特性——Lambda 表达式(2)

            Java Stream函数式编程接口最初在Java 8中引入,并且与 lambda 一起成为Java开发里程碑式的功能特性,它极大的方便了开放人员处理集合类数据的效率。         Java Stream就是一个数据流经的管道,并且在管道中对数据进行操作,然后流入下一个管道。有学过linux

    2024年02月11日
    浏览(39)
  • Java8新特性—Lambda表达式

    Java 8是Java编程语言的一个版本,于2014年发布。它引入了许多新的特性和改进。 Lambda表达式是Java 8中引入的一个重要的新特性,它提供了一种更加简洁、灵活的方式来编写函数式接口的实现,从而提高了代码的可读性和简洁性。 在本文中,我们将介绍Lambda表达式的基本语法、

    2024年02月03日
    浏览(42)
  • Java8新特性-Lambda表达式

    Lambda表达式 Lambda是一个匿名函数, 可以把lambda表达式理解为是一段可以传递的代码,(将代码像数据一样传递) 变化 需求: 求 薪资高于5000的员工信息 Lambda基本语法 在 java8 中引入了一个新的操作符 \\\"-\\\" , 箭头操作符, 箭头操作符 将Lambda表达式拆分为两部分: 左侧: Lambda表达式的参

    2024年02月01日
    浏览(38)
  • 【 C++11 】lambda表达式

    目录 1、lambda表达式的引入 2、lambda表达式         lambda表达式的语法         lambda表达式捕捉列表说明         使用lambda表达式排序自定义类型         lambda表达式的底层原理 1、lambda表达式的引入 在C++98中,如果想要对一个数据集合中的元素进行排序,可以

    2024年02月08日
    浏览(45)
  • C++ 11 Lambda表达式

    https://www.cnblogs.com/DswCnblog/p/5629165.html C++11的一大亮点就是引入了Lambda表达式。利用Lambda表达式,可以方便的定义和创建匿名函数。对于C++这门语言来说来说,“Lambda表达式”或“匿名函数”这些概念听起来好像很深奥,但很多高级语言在很早以前就已经提供了Lambda表达式的功

    2024年02月10日
    浏览(49)
  • C++11 lambda表达式

    lambda表达式是C++11或者更新版本的一个 语法糖 ,本身不是C++开发的。但是因其便利,很值得我们学习和使用。lambda有很多叫法,有lambda表达式、lambda函数、匿名函数,本文中为了方便表述统一用 lambda表达式 进行叙述。 在C++中,为了实现泛型编程,在一个类中,我们难免遇到

    2024年02月08日
    浏览(55)
  • C++11:lambda表达式

    lambda表达式实际上是一个匿名类的成员函数,该类由编译器为lambda创建,该函数被隐式地定义为内联。因此,调用lambda表达式相当于直接调用它的operator()函数,这个函数可以被编译器内联优化(建议)。 例如快速排序算法,STL允许用户自定义比较方式,在C++11之前,通常使用

    2024年02月14日
    浏览(29)
  • 【Java系列】JDK 1.8 新特性之 Lambda表达式

    Lambda是一个匿名函数,我们可以将Lambda表达式理解为一段可以传递的代码(将代码像数据一样传递)。使用它可以写出简洁、灵活的代码。作为一种更紧凑的代码风格,使java语言表达能力得到提升。 结果: ​19:43:39.303 [main] INFO com.chen.test.JAVA8Features.Demo01 - 我是没有使用Lambda表

    2024年02月22日
    浏览(48)
  • Java8新特性1——函数式接口&lambda表达式

    注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过 目录: Java8新特性1——函数式接口lambda表达式 Java8新特性2——方法引用 Java8新特性3——Stream 如果在一个接口中, 有且只有一个抽象方法 ,则该接口被称为函数式接口。如: 注: 可以在接口前使用 @FunctionalInt

    2024年02月10日
    浏览(43)
  • C++11_lambda表达式

    lambda表达式是C++11新引入的功能,它的用法与我们之前学过的C++语法有些不同。 [capture-list] (parameters) mutable - return-type { statement } [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来 判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的

    2024年02月02日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包