C++ Lambda表达式的基本使用

0.前言

Lambda表达式(也叫lambda函数,或简称lambda),是从C++ 11开始引入并不断完善的,是能够捕获作用域中变量的匿名函数对象。因为C++是不能嵌套定义函数的,所以lambda就成了我们构造闭包的主要手段,不过在对象的生命周期上还是有点不同。本文主要展示lambda的基本使用(一些我不常用或者新标准如C++20就先不做笔记了,以在线文档为准)。

目录

1.认识 Lambda

1.1.捕获列表 [ ]

1.2.形参列表 ( ) 

1.3.说明符 

1.4.返回类型 ->

1.5.函数体 { }

2.基本使用

3.参考


1.认识 Lambda

lambda的基本语法如下:

当定义一个lambda时,编译器生成一个与lambda对应的新的(未命名的)类类型。下面对重要的组成部分进行说明:

1.1.捕获列表 [ ]

捕获列表是零或多个捕获符的逗号分隔符列表,可选地以默认捕获符开始(仅有的默认捕获符是 & 和 = )。默认情况下,从lambda生成的类都包含一个对应该lambda所捕获变量的数据成员。类似任何普通类地数据成员,lambda的数据成员也在lambda对象创建时被初始化。类似参数传递,变量的捕获方式也可以是值或引用。

值捕获:

void func()
{
	int i = 100;//局部变量
	//将i拷贝到明位f的可调用对象
	auto f = [i] { return i; };
	i = 0;
	int j = f(); //j=100,因为i是创建时拷贝的
}

引用捕获:

void func()
{
	int i = 100;//局部变量
	//对象f包含i的引用
	auto f = [&i] { return i; };
	i = 0;
	int j = f(); //j=0,传递的是引用
}

除了自己列出捕获列表的变量,还可以让编译器根据lambda中代码来推断我们要使用哪些变量(隐式捕获),用过使用&或=指示编译器推断捕获列表。&则采用引用捕获的方式,=则采用值捕获的方式。混合使用隐式捕获和显示捕获,则两者须使用不同的方式,一个为引用捕获,一个为值捕获。

lambda捕获列表:

  • [ ]。空捕获列表,lambda不能使用所在函数中的变量。
  • [=]。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
  • [&]。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
  • [this]。函数体内可以使用Lambda所在类中的成员变量。
  • [a]。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
  • [&a]。将a按引用进行传递。
  • [=,&a, &b]。除a和b按引用进行传递外,其他参数都按值进行传递。
  • [&, a, b]。除a和b按值进行传递外,其他参数都按引用进行传递。

悬垂引用:

若以引用隐式或显式捕获非引用实体,而在该实体的生存期结束之后调用lambda对象的函数调用运算符,则发生未定义行为。C++ 的闭包并不延长被捕获的引用的生存期。这同样适用于被捕获的this指针所指向的对象的生存期。

1.2.形参列表 ( ) 

lambda形参列表和一般的函数形参列表类似,但不允许默认实参(C++14 前)。当以 auto 为形参类型时,该 lambda 为泛型 lambda(C++14 起)。与一个普通函数调用类似,调用一个lambda时给定的实参被用来初始化lambda的形参。

//代码在VS2019中测试
void func()
{
	int i = 1, j = 2;
	auto f = [](int a,int &b) {
		a = 10;
		b = 20;
		//输出:10  20
		std::cout << a << " " << b << std::endl;
	};
	f(i,j);
	//输出:1 20
	std::cout << i << " " << j << std::endl;
}
//代码在VS2019中测试
void func()
{
	auto f = [](auto a,int b=10) {
		std::cout << a << " " << b << std::endl;
	};
	f(1.5, 2);
	f(true);
}

1.3.说明符 

允许以下说明符:

  • mutable:允许 函数体 修改各个复制捕获的对象,以及调用其非 const 成员函数;
  • constexpr:显式指定函数调用运算符为 constexpr 函数。此说明符不存在时,若函数调用运算符恰好满足针对 constexpr 函数的所有要求,则它也会是 constexpr;  (C++17 起)
  • consteval:指定函数调用运算符为立即函数。不能同时使用 consteval 和 constexpr。(C++20 起)

默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。假如我们希望能改变一个被捕获的变量的值,就必须在参数列表后面加上关键字mutable。而一个引用捕获的变量则不受此限制。 

//代码在VS2019中测试
void func()
{
	int i = 10, j = 10;
	//加上mutable才可以在lambda函数中改变捕获的变量值
	auto f = [i, &j]() mutable {
		i = 100, j = 100;
	};
	i = 0, j = 0;
	f();
	//输出:0 100
	std::cout << i << " " << j << std::endl;
}

1.4.返回类型 ->

当我们需要为一个lambda定义返回类型时,需要使用尾置返回类型。返回类型若缺省,则根据函数体中的 return 语句进行推断(如果有多条return语句,需要保证类型一直,否则编译器无法自动推断)。默认情况下,如果一个lambda函数体不包含return语句,则编译器假定返回void。

void func()
{
	auto f = []() ->double {
		if (1 > 2)
			return 1;
		else 
			return 2.0;
	};
	std::cout << f() << std::endl;
}

如果不显示指定返回类型,则int和double两种返回类型会导致推断冲突。

1.5.函数体 { }

略,同普通函数的函数体。

2.基本使用

参照在线文档中的示例,此示例演示如何传递 lambda 给泛型算法,以及 lambda 表达式所产生的对象能如何存储于 std::function 对象。(已在VS2019中测试)

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

int main()
{
    std::vector<int> c = { 1, 2, 3, 4, 5, 6, 7 };
    int x = 5;
    c.erase(std::remove_if(c.begin(), c.end(), [x](int n) { return n < x; }), c.end());

    std::cout << "c: ";
    std::for_each(c.begin(), c.end(), [](int i) { std::cout << i << ' '; });
    std::cout << '\n';

    // 闭包的类型不能被指名,但可用 auto 提及
    // C++14 起,lambda 能拥有自身的默认实参
    auto func1 = [](int i = 6) { return i + 4; };
    std::cout << "func1: " << func1() << '\n';

    // 与所有可调用对象相同,闭包能可以被捕获到 std::function 之中
    // (这可能带来不必要的开销)
    std::function<int(int)> func2 = [](int i) { return i + 4; };
    std::cout << "func2: " << func2(6) << '\n';

    system("pause");
    return 0;
}

输出:

 

3.参考

参考书籍:《C++ Primer》中文第五版

在线文档:https://zh.cppreference.com/w/cpp/language/lambda

参考博客排版:https://www.cnblogs.com/jimodetiantang/p/9016826.html

 

  • 37
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
C++ Lambda表达式是一种简洁的匿名表示方法,可以在代码定义并使用Lambda表达式的格式一般为:[] (参数列表) mutable -> 返回值类型 { 表达式 }。Lambda表达式可以捕获外部变量,并将其作为参数传递给函数体部分进行处理。Lambda表达式使用可以作为函数对象、函数指针或者函数参数进行传递。 Lambda表达式的底层原理是通过生成一个匿名类来实现。该类会重载函数调用运算符(),并包含Lambda表达式的函数体。Lambda表达式捕获的外部变量会以成员变量的形式存储在该类。当Lambda表达式被调用,实际上是调用了该类的重载函数调用运算符()。 Lambda表达式可以与std::function结合使用,以实现函数对象的灵活使用。也可以将Lambda表达式赋值给相同类型的函数指针,实现函数指针的使用。但一般不建议这样使用,因为Lambda表达式已经提供了更加方便和简洁的方式。 总结来说,C++ Lambda表达式是一种用于定义匿名函数的语法,可以捕获外部变量并进行处理。其底层通过生成一个匿名类来实现,并提供了与std::function和函数指针的结合使用方式。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++11:lambda表达式](https://blog.csdn.net/zhang_si_hang/article/details/127117260)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚建波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值