【c++】:Lambda 表达式介绍和使用
C++17 对 Lambda 表达式进行了多项重要扩展,使其功能更强大、使用更灵活。Lambda 表达式是一种匿名函数,用于简化代码中的回调、排序、算法参数等场景。以下从 基础语法、C++17 新增特性、实际应用 三个维度详细讲解:
一、Lambda 表达式基础语法
Lambda 表达式的核心结构如下(C++11 起支持):
[capture-list](parameters) mutable noexcept -> return-type {// 函数体
}
- 捕获列表(
capture-list
):定义 Lambda 外部变量的访问方式(如按值捕获[x]
、按引用捕获[&y]
)。 - 参数列表(
parameters
):与普通函数参数类似,C++14 起支持auto
自动推导参数类型。 mutable
:允许修改按值捕获的变量(默认按值捕获为const
)。noexcept
:声明函数不抛出异常。- 返回类型(
return-type
):可省略,由编译器自动推导(C++11 起)。
二、C++17 对 Lambda 的核心增强
C++17 新增了多项特性,解决了之前版本的限制,主要包括:
1. ** constexpr Lambda(常量表达式 Lambda)**
- 特性:Lambda 表达式可在编译期执行,支持用于
constexpr
变量初始化、模板参数等编译期场景。 - 语法:无需显式声明
constexpr
,编译器会自动判断 Lambda 是否满足常量表达式要求(若函数体符合constexpr
规则)。 - 示例:
#include <iostream>int main() {// C++17 中,满足条件的 Lambda 自动为 constexprauto add = [](int a, int b) { return a + b; };// 编译期计算(需编译器支持 C++17)constexpr int sum = add(3, 5); // sum = 8(编译期确定)std::cout << sum << std::endl;return 0; }
- 限制:函数体只能包含编译期可执行的代码(如无动态内存分配、无虚函数调用等)。
2. 捕获 *this
(按值捕获当前对象)
- 问题背景:C++11/14 中,
[this]
按引用捕获当前对象,若 Lambda 生命周期超过对象生命周期,会导致悬垂引用。 - C++17 解决方案:
[*this]
按值捕获当前对象的副本,避免悬垂引用。 - 示例:
#include <iostream> #include <memory>struct MyClass {int x = 10;auto getLambda() {// 按值捕获当前对象(*this 是对象的副本)return [*this]() { std::cout << "x = " << x << std::endl; // 访问副本的 x};} };int main() {auto lambda = [](){MyClass obj;return obj.getLambda(); // obj 生命周期结束,但 Lambda 持有副本}();lambda(); // 输出:x = 10(安全访问副本)return 0; }
3. 模板化 Lambda(泛型 Lambda 增强)
- 特性:C++14 已支持
auto
作为参数类型(泛型 Lambda),C++17 进一步允许显式模板参数列表,支持更复杂的泛型逻辑。 - 语法:
[]<template-params>(params) { ... }
- 示例:
#include <iostream> #include <vector> #include <list>int main() {// 显式模板参数的 Lambda(C++17 新增)auto printSize = []<typename T>(const T& container) {std::cout << "Size: " << container.size() << std::endl;};std::vector<int> vec = {1, 2, 3};std::list<double> lst = {1.1, 2.2};printSize(vec); // 输出:Size: 3(T 推导为 vector<int>)printSize(lst); // 输出:Size: 2(T 推导为 list<double>)return 0; }
- 优势:比
auto
参数更灵活,支持模板特化、模板参数约束(C++20 进一步增强)等。
4. Lambda 在非类型模板参数中使用
- 特性:C++17 允许 Lambda 作为非类型模板参数(需满足
constexpr
要求)。 - 示例:
#include <iostream>// 模板接受 Lambda 作为参数 template <auto Lambda> void callLambda() {Lambda(); // 调用传入的 Lambda }int main() {// 传递 constexpr Lambda 作为模板参数callLambda<[](){ std::cout << "Lambda called!\n"; }>(); // 输出:Lambda called!return 0; }
5. 捕获初始化(初始化捕获的增强)
- 特性:C++14 已支持初始化捕获(如
[x = 10](){ ... }
),C++17 允许捕获中使用auto
自动推导类型,简化代码。 - 示例:
#include <iostream> #include <string>int main() {std::string s = "hello";// 初始化捕获 + auto 推导(C++17 允许)auto lambda = [str = std::move(s)]() { // str 类型自动推导为 stringstd::cout << str << std::endl;};lambda(); // 输出:helloreturn 0; }
三、C++17 Lambda 的实际应用场景
1. 编译期计算
利用 constexpr
Lambda 实现编译期逻辑,提升运行时效率:
constexpr auto factorial = [](int n) {int res = 1;for (int i = 2; i <= n; ++i) res *= i;return res;
};constexpr int f5 = factorial(5); // 120(编译期计算)
2. 安全捕获对象
使用 [*this]
避免对象生命周期问题,尤其在异步编程中:
#include <future>struct Task {int value = 5;auto asyncTask() {// 按值捕获 this,确保异步执行时对象已销毁也安全return std::async(std::launch::async, [*this]() {return value * 2; // 使用对象副本的 value});}
};int main() {auto future = [](){Task t;return t.asyncTask(); // t 销毁,但 Lambda 持有副本}();std::cout << future.get() << std::endl; // 输出:10return 0;
}
3. 泛型算法适配
结合模板化 Lambda 简化泛型代码,适配不同容器或数据类型:
#include <algorithm>
#include <vector>// 通用过滤函数,接受 Lambda 作为过滤条件
template <typename Container, typename Filter>
auto filter(const Container& c, Filter f) {Container res;std::copy_if(c.begin(), c.end(), std::back_inserter(res), f);return res;
}int main() {std::vector<int> nums = {1, 2, 3, 4, 5, 6};// 模板化 Lambda 作为过滤条件(同时支持奇偶数判断)auto isEven = [](auto x) { return x % 2 == 0; };auto evens = filter(nums, isEven); // evens = {2,4,6}return 0;
}
四、C++17 Lambda 与之前版本的对比
特性 | C++11/14 支持度 | C++17 支持度 |
---|---|---|
constexpr Lambda | 不支持(需手动包装) | 自动支持(符合条件时) |
按值捕获 *this | 不支持(仅 [this] 按引用) | 支持 [*this] 按值捕获副本 |
显式模板参数 | 不支持(仅 auto 参数) | 支持 <typename T> 模板参数 |
作为非类型模板参数 | 不支持 | 支持(需 constexpr ) |
初始化捕获 auto | 部分支持(需显式类型) | 完全支持 auto 推导 |
总结
C++17 显著增强了 Lambda 表达式的功能,使其从“简单匿名函数”升级为支持 编译期计算、安全对象捕获、复杂泛型逻辑 的强大工具。这些特性尤其在模板编程、异步编程、泛型算法中能大幅简化代码,提升可读性和安全性。掌握 C++17 Lambda 是现代 C++ 开发的重要技能。