欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > [C++11] Lambda 表达式

[C++11] Lambda 表达式

2024/12/29 10:20:35 来源:https://blog.csdn.net/SDFsoul/article/details/143663029  浏览:    关键词:[C++11] Lambda 表达式

lambda 表达式(Lambda Expressions)作为一种匿名函数,为开发者提供了简洁、灵活的函数定义方式。相比传统的函数指针和仿函数,lambda 表达式在简化代码结构、提升代码可读性和编程效率方面表现出色。


Lambda 表达式的基本语法

在 C++ 中,lambda 表达式的格式如下:

[capture-list] (parameters) -> return type {function body
}

各部分含义:

  • **[capture-list]**:捕捉列表,指定lambda表达式可以访问的外部变量。捕捉列表,该列表总是出现在 <font style="color:rgb(31,35,41);">lambda</font> 函数的开始位置,编译器根据[]来判断接下来的代码是否为 <font style="color:rgb(31,35,41);">lambda</font> 函数,捕捉列表能够捕捉上下⽂中的变量供 <font style="color:rgb(31,35,41);">lambda</font> 函数使⽤,捕捉列表可以传值传引⽤捕捉,捕捉列表为空也不能省略
  • **(parameters)**:参数列表,类似普通函数的参数,如果不需要参数传递,则可以连同<font style="color:rgb(31,35,41);">( )</font>⼀起省略。
  • **-> return type**:返回值类型。通常可以省略,由编译器推导。
  • **function body**:函数体,实现具体的功能。
// 1、捕捉为空也不能省略
// 2、参数为空可以省略
// 3、返回值可以省略,可以通过返回对象⾃动推导
// 4、函数题不能省略
auto func1 = []
{cout << "hello bit" << endl;return 0;
};

示例代码

以下是一个简单的 lambda 表达式示例:

auto add = [](int x, int y) -> int {return x + y;
};
std::cout << add(3, 5) << std::endl; // 输出:8

捕捉列表的使用

捕捉列表的分类

捕捉列表可以在 lambda 表达式中允许访问外部作用域的变量。捕捉方式主要包括:

  1. 显式捕捉:在捕捉列表中显⽰的传值捕捉和传引⽤捕捉,捕捉的多个变量⽤逗号分割,例如在捕捉列表中使用传值[x, y]或传引用[&z]捕捉变量。
  2. 隐式捕捉:使用 =(按值捕捉)或 &(按引用捕捉)进行隐式捕捉,这样我们 lambda 表达式中⽤了哪些变量,编译器就会⾃动捕捉那些变量,底层汇编也是这样的。
  3. 混合捕捉:可混合显式与隐式捕捉,<font style="color:rgb(31,35,41);">[=, &x]</font>表⽰其他变量隐式值捕捉,x引⽤捕捉;<font style="color:rgb(31,35,41);">[&, x, y]</font>表⽰其他变量引⽤捕捉,x和y值捕捉。当使⽤混合捕捉时,第⼀个元素必须是&或=,并且&混合捕捉时,后⾯的捕捉变量必须是值捕捉,同理=混合捕捉时,后⾯的捕捉变量必须是引⽤捕捉。

使用时的注意事项

  1. 全局位置的<font style="color:rgb(31,35,41);">lambda</font>

lambda 表达式如果在函数局部域中,他可以捕捉 lambda 位置之前定义的变量。但是不能捕捉静态局部变量和全局变量,静态局部变量和全局变量也不需要捕捉,lambda 表达式中可以直接使⽤。这也意味着 lambda 表达式如果定义在全局位置,捕捉列表不需要捕捉任何变量,必须为空

// 全局
auto f1 = [](int a, int b){return a + b;
}
  1. mutable修饰lambda

默认情况下, lambda 捕捉列表是被const修饰的,也就是说传值捕捉的过来的对象不能修改,mutable加在参数列表的后⾯可以取消其常量性,也就说使⽤该修饰符后,传值捕捉的对象就可以修改了,但是修改还是形参对象,不会影响实参。使⽤该修饰符后,参数列表不可省略(即使参数为空)。

int a = 5, b = 10;
auto func = [=]() mutable {a++;         // 可以修改按引用捕捉的变量b++;
};

注意:默认情况下捕捉的变量为 const,无法在 lambda 中修改,除非在参数列表后加 mutable 修饰符,如上例中的 mutable

捕捉的具体示例

  1. 显式捕捉:
int a = 0, b = 1, c = 2, d = 3;
auto func1 = [a, &b]{// 值捕捉的变量不能修改,引⽤捕捉的变量可以修改//a++;b++;int ret = a + b;return ret;};
cout << func1() << endl;
  1. 隐式值捕捉 / 隐式引用捕捉:
// 隐式值捕捉
// ⽤了哪些变量就捕捉哪些变量
auto func2 = [=] {int ret = a + b + c;return ret;};
cout << func2() << endl;// 隐式引用捕捉
// ⽤了哪些变量就捕捉哪些变量
auto func3 = [&] {a++;c++;d++;};
func3();
cout << a << " " << b << " " << c << " " << d << endl;
  1. 混合捕捉:
// 混合捕捉1
auto func4 = [&, a, b] {//a++;//b++;c++;d++;return a + b + c + d;};
func4();
cout << a << " " << b << " " << c << " " << d << endl;// 混合捕捉2
auto func5 = [=, &a, &b] {a++;b++;/*c++;d++;*/return a + b + c + d;};
func5();
cout << a << " " << b << " " << c << " " << d << endl;

Lambda 表达式的应用场景

常见应用

  • 排序函数中的比较器:利用 lambda 表达式可简化排序代码。
  • 自定义线程执行函数:lambda 可定义线程任务,便于封装。
  • 智能指针的删除器:lambda 表达式可以方便地作为 unique_ptr 等的自定义删除器。

示例:用于商品排序的 Lambda 表达式

相当于直接替代仿函数来使用。

struct Goods {std::string name;double price;int rating;Goods(const std::string &n, double p, int r) : name(n), price(p), rating(r) {}
};std::vector<Goods> items = {{"苹果", 2.5, 5}, {"橙子", 3.0, 4}, {"香蕉", 1.5, 3}};// 使用 lambda 表达式按价格升序排序
std::sort(items.begin(), items.end(), [](const Goods &a, const Goods &b) {return a.price < b.price;
});

Lambda 表达式的原理

Lambda 表达式在底层通过创建一个仿函数对象来实现。当我们定义一个 lambda 表达式时,编译器会生成一个包含捕捉列表和函数体的匿名类,lambda 表达式实际上是该类的一个 operator(),底层是仿函数对象。

汇编层的实现

通过汇编代码可看到,lambda 表达式生成的对象会自动调用 operator(),并且捕捉的变量会作为构造函数的参数。如下所示:

class Rate
{
public:Rate(double rate): _rate(rate){}double operator()(double money, int year){return money * _rate * year;}private:double _rate;
};int main()
{double rate = 0.49;// lambdaauto r2 = [rate](double money, int year) {return money * rate * year;};// 函数对象Rate r1(rate);r1(10000, 2); // 仿函数r2(10000, 2); // lambdaauto func1 = [] {std:: cout << "hello world" << std::endl;};func1();return 0;
}

call的内容不难看出lambda的底层就是仿函数。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com