▒ 目录 ▒
- 🛫 问题
- 描述
- 环境
- 1️⃣ 什么是Lambda表达式
- Lambda 表达式的各个部分
- 2️⃣ 优缺点
- 优点
- 缺点
- 3️⃣ 使用场景
- 在线C++工具
- STL算法库
- STL容器中需要传递比较函数(示例失败了)
- 多线程示例
- 4️⃣ Lambda表达式与函数指针的比较
- 5️⃣ 捕获列表
- 6️⃣ 返回值类型
- 7️⃣ 工作原理
- 📖 参考资料
🛫 问题
描述
记得去年立了一个重学C++新特性的flag,可是真的太忙了,大部分精力都花在全栈上了,今年开始看一些开源源码,发现各种奇怪的语法,根本看不懂,
不学不行
了。而且接触了很多语言后,发现新特性的确能提高开发效率
,所以还是重新学习下C++吧。
环境
版本号 | 描述 | |
---|---|---|
文章日期 | 2023-06-09 | |
操作系统 | Win11 - 21H2 - 22000.1335 | |
C++在线工具 | https://c.runoob.com/compile/12/ | |
1️⃣ 什么是Lambda表达式
Lambda表达式是C++11中新增的一种
函数对象
,它可以方便地定义一个匿名函数,从而简化代码的编写。
Lambda表达式的本质是一个可调用对象
,可以像函数一样被调用,也可以作为函数参数或返回值。
Lambda 表达式的各个部分
下面是作为第三个参数 std::sort() 传递给函数的简单 lambda:
#include <algorithm> #include <cmath> void abssort(float* x, unsigned n) { std::sort(x, x + n, // 下面是一个简单的 `Lambda 表达式` [](float a, float b) { return (std::abs(a) < std::abs(b)); } ); }
下图显示了 lambda 语法的各个部分:
捕获列表
:(capture list)(在 C++ 规范中也称为 Lambda 引导。)参数列表
:(parameters list)(可选)。 (也称为 Lambda 声明符)mutable 规范
:(可选)。异常说明
:exception-specification(可选)。返回类型
:trailing-return-type(可选)。Lambda 体
:也就是函数体。
引用大佬的一张图:
2️⃣ 优缺点
优点
- 简化代码:Lambda表达式可以将一些冗长的代码简化为一行代码,使代码更加简洁。
- 提高可读性:Lambda表达式可以使代码更加易读,减少了一些冗余的代码,使代码更加简洁明了。
- 提高可维护性:Lambda表达式可以使代码更加易于维护,因为它可以将一些复杂的逻辑封装在一个方法中,使代码更加模块化。
缺点
- 学习成本高:Lambda表达式需要一定的学习成本,需要理解函数式编程的概念和Lambda表达式的语法。
- 可读性降低:有时候Lambda表达式可能会使代码变得更加难以理解,特别是当Lambda表达式嵌套时。
- 性能问题:Lambda表达式可能会影响程序的性能,因为它需要创建一个新的对象来表示Lambda表达式。但是,这种影响通常是微不足道的,只有在极端情况下才会有明显的性能问题。
3️⃣ 使用场景
Lambda表达式可以用于任何需要函数对象的场景,例如:
- STL算法中需要传递函数对象的地方,如
std::sort
、std::for_each
等;- STL容器中需要传递比较函数的地方,如
std::set
、std::map
等;- 多线程编程中需要传递回调函数的地方,如
std::thread
、std::async
等。
在线C++工具
为了方便演示,找了个在线C++工具 https://c.runoob.com/compile/12/ ,可以直接在网页中运行C++代码。
效果图如下:
STL算法库
find_if
应用实例
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
int main()
{
int x = 5;
int y = 10;
deque<int> coll = { 1, 3, 19, 5, 13, 7, 11, 2, 17 };
auto pos = find_if(coll.cbegin(), coll.cend(), [=](int i) {
return i > x && i < y;
});
cout << "find " << (pos != coll.end() ? "success" : "failed");
return 0;
}
sort
实例,用于对一个整数数组进行排序:
以上代码中,Lambda表达式[](int a, int b) { return a < b; }
用于指定排序规则,即按照升序排列。
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
// 使用Lambda表达式对vec进行排序
std::sort(vec.begin(), vec.end(), [](int a, int b) { return a < b; });
// 输出排序后的结果
// 1 1 2 3 3 4 5 5 5 6 9
for (auto x : vec)
{
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
STL容器中需要传递比较函数(示例失败了)
下面代码不能在
C++在线工具
上正常运行,vs2022上也未能正常运行,以后再研究吧,实在没整明白。
#include <iostream>
#include <algorithm>
#include <string>
#include <map>
int main()
{
auto fc = [](const std::string& a, const std::string& b) {
return a.length() < b.length();
};
std::map<std::string, int, decltype(fc)*> myMap = {{"apple", 5}, {"banana0", 10}, {"orange", 15}};
// 使用迭代器遍历map
std::cout << "使用迭代器遍历map:" << std::endl;
for (auto it = myMap.begin(); it != myMap.end(); ++it) {
std::cout << it->first << " : " << it->second << std::endl;
}
// 使用范围for循环遍历map
// std::cout << "使用范围for循环遍历map:" << std::endl;
// for (const auto& [key, value] : myMap) {
// std::cout << key << " : " << value << std::endl;
// }
return 0;
}
多线程示例
#include <iostream>
#include <thread>
#include <vector>
#include <algorithm>
int main()
{
// vector 容器存储线程
std::vector<std::thread> workers;
for (int i = 0; i < 5; i++)
{
workers.push_back(std::thread([]()
{
std::cout << "thread function\n";
}));
}
std::cout << "main thread\n";
// 通过 for_each 循环每一个线程
// 第三个参数赋值一个task任务
// 符号'[]'会告诉编译器我们正在用一个匿名函数
// lambda函数将它的参数作为线程的引用t
// 然后一个一个的join
std::for_each(workers.begin(), workers.end(), [](std::thread &t;)
{
t.join();
});
return 0;
}
4️⃣ Lambda表达式与函数指针的比较
Lambda表达式与函数指针类似,都可以用于定义函数对象。但是,Lambda表达式相比函数指针具有以下优点:
- Lambda表达式可以捕获外部变量,从而方便地访问外部环境;
- Lambda表达式可以定义在函数内部,从而避免了命名冲突的问题;
- Lambda表达式可以使用auto关键字自动推导返回值类型,从而简化代码。
5️⃣ 捕获列表
Lambda表达式的捕获列表用于指定Lambda表达式中使用的外部变量。捕获列表可以为空,也可以包含以下内容:
[]
:不捕获任何外部变量;[&]
:以引用方式捕获所有外部变量;[=]
:以值方式捕获所有外部变量;[var1, var2, ...]
:指定捕获特定的外部变量;[&, var1, var2, ...]
:以引用方式捕获所有外部变量,并指定捕获特定的外部变量;[=, &var1, &var2, ...]
:以值方式捕获所有外部变量,并以引用方式捕获特定的外部变量。
6️⃣ 返回值类型
Lambda表达式的返回值类型可以显式指定,也可以使用auto关键字自动推导。如果Lambda表达式的函数体只有一条语句,且该语句的返回值类型可以自动推导,则可以省略返回值类型和return关键字。
7️⃣ 工作原理
编译器会把一个Lambda表达式生成一个匿名类的匿名对象,并在类中重载函数调用运算符,实现了一个operator()方法。
以auto print = []{cout << "Hello World!" << endl; };
为例,编译器会把上面这一句翻译为下面的代码:
class print_class
{
public:
void operator()(void) const
{
cout << "Hello World!" << endl;
}
};
// 用构造的类创建对象,print此时就是一个函数对象
auto print = print_class();
ps: 仿函数(functor)又称为函数对象(function object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载operator()运算符,仿函数与Lamdba表达式的作用是一致的。
stl中含有大量类似的对象,如std::less
:
📖 参考资料
- 微软Lambda教程 https://learn.microsoft.com/zh-cn/cpp/cpp/lambda-expressions-in-cpp?view=msvc-170
- C++ Lambda表达式详解 https://blog.csdn.net/qq_37085158/article/details/124626913
- 在线C++工具 https://c.runoob.com/compile/12/