C++ Lambda 表达式
·
C++ Lambda 表达式
目录
- #1-lambda-表达式基本概念
- #2-lambda-表达式完整语法
- #3-捕获列表详解
- #4-参数列表与可变规范
- #5-返回类型与异常规范
- #6-lambda-与-stl-算法
- #7-高级特性与技巧
- #8-性能考虑与最佳实践
1. Lambda 表达式基本概念
1.1 什么是 Lambda 表达式?
Lambda 表达式是 C++11 引入的匿名函数对象,允许在代码中内联定义函数。
基本语法:
参数列表 -> 返回类型 { 函数体 }
1.2 最简单的 Lambda 表达式
#include <iostream>
int main() {
// 最简单的 lambda:无参数,无返回值
{
std::cout << "Hello, Lambda!" << std::endl;
}(); // 直接调用
// 省略空参数列表的括号
[]{
std::cout << "Simplified lambda" << std::endl;
}();
return 0;
}
1.3 将 Lambda 赋值给变量
#include <iostream>
#include <functional>
int main() {
// 使用 auto 推导 lambda 类型
auto greet = {
std::cout << "Hello from lambda!" << std::endl;
};
greet(); // 调用 lambda
greet(); // 可以多次调用
// 带参数的 lambda
auto add = int a, int b {
return a + b;
};
std::cout << "5 + 3 = " << add(5, 3) << std::endl;
return 0;
}
2. Lambda 表达式完整语法
2.1 完整语法结构
[ 捕获列表 ] ( 参数列表 ) 可变规范(可选) 异常规范(可选) -> 返回类型(可选) { 函数体 }
完整示例:
#include <iostream>
int main() {
int x = 10;
int y = 20;
// 完整的 lambda 表达式
auto complex_lambda = int a, int b mutable
noexcept -> int {
x = a + b; // 修改捕获的 x(副本)
y = a - b; // 修改捕获的 y(引用)
return x * y;
};
int result = complex_lambda(5, 3);
std::cout << "Result: " << result << std::endl;
std::cout << "Original y: " << y << std::endl; // y 被修改
return 0;
}
3. 捕获列表详解
3.1 值捕获 vs 引用捕获
值捕获 [=] 或 [变量名]
#include <iostream>
int main() {
int a = 10, b = 20;
// 值捕获:创建变量的副本
auto value_capture = {
std::cout << "a = " << a << ", b = " << b << std::endl;
// a = 5; // 错误:值捕获的变量默认是 const
};
a = 100; // 修改原始变量
value_capture(); // 输出:a = 10, b = 20(捕获时的值)
return 0;
}
引用捕获 [&] 或 [&变量名]
#include <iostream>
int main() {
int a = 10, b = 20;
// 引用捕获:使用变量的引用
auto ref_capture = {
std::cout << "a = " << a << ", b = " << b << std::endl;
a = 100; // 修改原始变量
};
ref_capture(); // 输出:a = 10, b = 20
std::cout << "After lambda: a = " << a << std::endl; // a = 100
return 0;
}
3.2 隐式捕获
隐式值捕获 [=]
int x = 1, y = 2, z = 3;
// 捕获所有外部变量(值方式)
auto lambda1 = {
std::cout << x + y + z << std::endl; // 可以使用所有外部变量
// 但不能修改,因为都是值捕获的副本
};
隐式引用捕获 [&]
int x = 1, y = 2, z = 3;
// 捕获所有外部变量(引用方式)
auto lambda2 = {
x = 10; y = 20; z = 30; // 修改原始变量
};
3.3 混合捕获
#include <iostream>
int main() {
int a = 1, b = 2, c = 3, d = 4;
// 混合捕获:a 值捕获,其他引用捕获
auto mixed1 = {
std::cout << "a(copy) = " << a << ", b(ref) = " << b
<< ", c(ref) = " << c << ", d(ref) = " << d << std::endl;
};
// 使用隐式+显式混合
auto mixed2 = { // 除 b 外都值捕获,b 引用捕获
// a, c, d 是副本,b 是引用
};
auto mixed3 = { // 除 a 外都引用捕获,a 值捕获
// b, c, d 是引用,a 是副本
};
mixed1();
return 0;
}
3.4 初始化捕获(C++14)
移动捕获
#include <memory>
#include <iostream>
int main() {
auto ptr = std::make_unique<int>(42);
// C++14 初始化捕获:移动语义
auto lambda = { // p 是移动捕获的 unique_ptr
if (p) {
std::cout << "Value: " << *p << std::endl;
}
};
lambda();
// ptr 现在为 nullptr
return 0;
}
在捕获时进行计算
#include <iostream>
int main() {
int x = 10;
int y = 20;
// 捕获时进行计算
auto lambda = {
std::cout << "Sum: " << sum << ", Product: " << product << std::endl;
};
lambda();
return 0;
}
3.5 this 指针捕获
#include <iostream>
class MyClass {
private:
int value = 42;
public:
void demonstrate_lambda() {
// 捕获 this 指针,访问成员变量
auto lambda1 = {
std::cout << "Value: " << this->value << std::endl;
};
// 隐式捕获 this(C++20 弃用 [=] 捕获 this)
auto lambda2 = { // C++17:复制 *this
std::cout << "Copied value: " << value << std::endl;
};
lambda1();
lambda2();
}
};
int main() {
MyClass obj;
obj.demonstrate_lambda();
return 0;
}
4. 参数列表与可变规范
4.1 参数列表
基本参数传递
#include <iostream>
int main() {
// 带参数的 lambda
auto adder = int a, int b -> int {
return a + b;
};
std::cout << "5 + 3 = " << adder(5, 3) << std::endl;
// 默认参数(C++14)
auto multiplier = int a, int b = 2 {
return a * b;
};
std::cout << "5 * 2 = " << multiplier(5) << std::endl;
std::cout << "5 * 3 = " << multiplier(5, 3) << std::endl;
return 0;
}
通用 Lambda(C++14)
#include <iostream>
#include <typeinfo>
int main() {
// 使用 auto 参数(通用 lambda)
auto print_type = auto value {
std::cout << "Value: " << value
<< ", Type: " << typeid(value).name() << std::endl;
};
print_type(42); // int
print_type(3.14); // double
print_type("Hello"); // const char*
// 多个 auto 参数
auto add_any = auto a, auto b {
return a + b;
};
std::cout << add_any(5, 3) << std::endl; // 8
std::cout << add_any(2.5, 3.7) << std::endl; // 6.2
return 0;
}
4.2 可变规范 (mutable)
修改值捕获的变量
#include <iostream>
int main() {
int counter = 0;
// 没有 mutable:不能修改值捕获的变量
// auto bad_lambda = {
// counter++; // 错误:counter 是 const
// };
// 使用 mutable:可以修改值捕获的变量(修改的是副本)
auto good_lambda = mutable {
counter++;
std::cout << "Internal counter: " << counter << std::endl;
};
good_lambda(); // 输出:Internal counter: 1
good_lambda(); // 输出:Internal counter: 2
std::cout << "Original counter: " << counter << std::endl; // 仍然是 0
return 0;
}
5. 返回类型与异常规范
5.1 返回类型推导与显式指定
自动返回类型推导
#include <iostream>
int main() {
// 返回类型自动推导为 int
auto add = int a, int b {
return a + b; // 编译器推导返回类型为 int
};
// 返回类型自动推导为 double
auto divide = double a, double b {
if (b != 0) return a / b;
else return 0.0; // 所有返回路径必须类型一致
};
std::cout << add(5, 3) << std::endl; // 8
std::cout << divide(10.0, 3.0) << std::endl; // 3.333...
return 0;
}
显式指定返回类型
#include <iostream>
#include <vector>
int main() {
// 需要显式指定返回类型的情况
// 1. 多条返回路径,类型不同但兼容
auto safe_divide = int a, int b -> double {
if (b == 0) return 0.0; // double
return a / b; // int,但会被转换为 double
};
// 2. 复杂返回类型
std::vector<int> numbers = {1, 2, 3};
auto get_iterator = -> std::vector<int>::iterator {
return numbers.begin();
};
// 3. Lambda 体复杂,编译器难以推导
auto complex_logic = int x -> int {
if (x > 0) return x * 2;
else if (x < 0) return x / 2;
else return 0;
};
std::cout << safe_divide(5, 2) << std::endl; // 2.5
std::cout << complex_logic(5) << std::endl; // 10
return 0;
}
5.2 异常规范
noexcept 规范
#include <iostream>
int main() {
// 不抛出异常的 lambda
auto no_throw = noexcept {
std::cout << "This lambda doesn't throw" << std::endl;
};
// 可能抛出异常的 lambda
auto may_throw = int x {
if (x < 0) {
throw std::invalid_argument("Negative value");
}
return x * 2;
};
try {
no_throw();
std::cout << may_throw(5) << std::endl;
// std::cout << may_throw(-1) << std::endl; // 会抛出异常
} catch (const std::exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
return 0;
}
6. Lambda 与 STL 算法
6.1 与 std::function 配合使用
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
// 接受函数作为参数
void process_numbers(const std::vector<int>& numbers,
std::function<void(int)> processor) {
for (int n : numbers) {
processor(n);
}
}
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 将 lambda 传递给函数
process_numbers(numbers, int n {
std::cout << n * 2 << " ";
});
std::cout << std::endl;
// 存储 lambda 在 std::function 中
std::function<int(int, int)> operation;
operation = int a, int b { return a + b; };
std::cout << "Addition: " << operation(5, 3) << std::endl;
operation = int a, int b { return a * b; };
std::cout << "Multiplication: " << operation(5, 3) << std::endl;
return 0;
}
6.2 在 STL 算法中的应用
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9, 3};
// 1. 使用 lambda 进行排序
std::sort(numbers.begin(), numbers.end(), int a, int b {
return a > b; // 降序排序
});
std::cout << "Sorted (descending): ";
for (int n : numbers) {
std::cout << n << " ";
}
std::cout << std::endl;
// 2. 使用 lambda 进行查找
auto it = std::find_if(numbers.begin(), numbers.end(), int n {
return n % 2 == 0; // 查找第一个偶数
});
if (it != numbers.end()) {
std::cout << "First even number: " << *it << std::endl;
}
// 3. 使用 lambda 进行转换
std::vector<int> squared;
std::transform(numbers.begin(), numbers.end(),
std::back_inserter(squared), int n {
return n * n;
});
std::cout << "Squared: ";
for (int n : squared) {
std::cout << n << " ";
}
std::cout << std::endl;
// 4. 使用 lambda 进行累加
int sum = std::accumulate(numbers.begin(), numbers.end(), 0,
int total, int n {
return total + n;
});
std::cout << "Sum: " << sum << std::endl;
return 0;
}
6.3 在智能指针中的应用
#include <iostream>
#include <memory>
#include <vector>
class Resource {
public:
Resource(int id) : id_(id) {
std::cout << "Resource " << id_ << " created" << std::endl;
}
~Resource() {
std::cout << "Resource " << id_ << " destroyed" << std::endl;
}
void use() {
std::cout << "Using resource " << id_ << std::endl;
}
private:
int id_;
};
int main() {
std::vector<std::unique_ptr<Resource>> resources;
// 创建一些资源
for (int i = 0; i < 3; ++i) {
resources.push_back(std::make_unique<Resource>(i));
}
// 使用 lambda 处理资源
std::for_each(resources.begin(), resources.end(),
auto& resource {
if (resource) {
resource->use();
}
});
// 使用 lambda 自定义删除器
auto custom_deleter = Resource* res {
std::cout << "Custom delete for resource" << std::endl;
delete res;
};
std::unique_ptr<Resource, decltype(custom_deleter)>
custom_resource(new Resource(99), custom_deleter);
return 0;
}
7. 高级特性与技巧
7.1 模板 Lambda(C++20)
#include <iostream>
#include <vector>
#include <list>
// C++20 模板 lambda
auto print_container = []<typename T>(const T& container) {
for (const auto& item : container) {
std::cout << item << " ";
}
std::cout << std::endl;
};
// 带模板参数的 lambda
auto add_template = []<typename T>(T a, T b) -> T {
return a + b;
};
int main() {
std::vector<int> vec = {1, 2, 3};
std::list<double> lst = {1.1, 2.2, 3.3};
print_container(vec); // 用于 vector<int>
print_container(lst); // 用于 list<double>
std::cout << add_template(5, 3) << std::endl; // int
std::cout << add_template(2.5, 3.7) << std::endl; // double
return 0;
}
7.2 立即调用 Lambda(IILE)
#include <iostream>
#include <string>
int main() {
// 立即调用的 lambda 表达式 (IILE)
const std::string message = -> std::string {
std::string result;
result += "Hello, ";
result += "World!";
return result;
}(); // 立即调用
std::cout << message << std::endl;
// 用于复杂初始化
const int computed_value = int base {
int result = base * 2;
result += 10;
return result * 3;
}(5); // 基于参数 5 立即计算
std::cout << "Computed value: " << computed_value << std::endl;
return 0;
}
7.3 递归 Lambda
#include <iostream>
int main() {
// 递归 lambda 需要使用 std::function
std::function<int(int)> factorial;
factorial = int n -> int {
if (n <= 1) return 1;
return n * factorial(n - 1);
};
std::cout << "5! = " << factorial(5) << std::endl;
// 使用泛型 lambda 和 auto 递归(C++14)
auto fib = auto&& self, int n -> int {
if (n <= 1) return n;
return self(self, n - 1) + self(self, n - 2);
};
auto fibonacci = int n { return fib(fib, n); };
std::cout << "fib(10) = " << fibonacci(10) << std::endl;
return 0;
}
7.4 可变参数 Lambda(C++14)
#include <iostream>
int main() {
// 可变参数 lambda
auto print_all = auto&&... args {
(std::cout << ... << args) << std::endl; // C++17 折叠表达式
};
print_all("Hello", " ", "World", " ", 2023);
// 更复杂的可变参数处理
auto sum_all = auto first, auto... rest {
if constexpr (sizeof...(rest) == 0) {
return first;
} else {
return first + sum_all(rest...);
}
};
// 使用立即调用的 lambda 包装递归可变参数
auto sum_wrapper = auto... args {
auto impl = auto&& self, auto first, auto... rest {
if constexpr (sizeof...(rest) == 0) {
return first;
} else {
return first + self(self, rest...);
}
};
return impl(impl, args...);
};
std::cout << "Sum: " << sum_wrapper(1, 2, 3, 4, 5) << std::endl;
return 0;
}
8. 性能考虑与最佳实践
8.1 性能优化
优先使用自动类型推导
#include <chrono>
#include <functional>
#include <iostream>
void performance_test() {
auto fast_lambda = int x { return x * x; }; // 快速:auto 推导
std::function<int(int)> slow_wrapper = fast_lambda; // 较慢:类型擦除
// 在性能敏感的代码中直接使用 auto lambda
auto start = std::chrono::high_resolution_clock::now();
int result = 0;
for (int i = 0; i < 1000000; ++i) {
result += fast_lambda(i); // 通常会被内联
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "Time with auto lambda: " << duration.count() << " microseconds" << std::endl;
}
避免不必要的捕获
#include <iostream>
void efficient_lambda_use() {
int important_data = 42;
std::string large_object = "very large string data...";
// 不好:捕获了不需要的大对象
// auto inefficient = {
// std::cout << important_data << std::endl; // 只需要这个
// // 但 large_object 也被不必要地捕获了
// };
// 好:只捕获需要的变量
auto efficient = { // 只捕获需要的变量
std::cout << important_data << std::endl;
};
efficient();
}
8.2 最佳实践总结
代码可读性
#include <vector>
#include <algorithm>
#include <iostream>
void readability_examples() {
std::vector<int> numbers = {5, 2, 8, 1, 9};
// 好:有意义的变量名和清晰的结构
auto is_even = int n { return n % 2 == 0; };
auto square = int n { return n * n; };
// 不好:过于复杂的内联 lambda
// std::remove_if(numbers.begin(), numbers.end(),
// int n{return n%2==0&&n>5||n<3&&n!=1;});
// 好:分解复杂逻辑
auto complex_condition = int n {
bool is_even = (n % 2 == 0);
bool is_large_even = is_even && (n > 5);
bool is_small_odd = !is_even && (n < 3) && (n != 1);
return is_large_even || is_small_odd;
};
}
// 使用有意义的 lambda 变量名
class DataProcessor {
private:
std::vector<int> data_;
public:
void process() {
// 有意义的 lambda 命名
auto validate_data = {
return !data_.empty() && data_.size() < 1000;
};
auto transform_data = int value {
return value * 2 + 1;
};
if (validate_data()) {
std::transform(data_.begin(), data_.end(),
data_.begin(), transform_data);
}
}
};
避免常见陷阱
#include <iostream>
#include <functional>
#include <memory>
void avoid_common_pitfalls() {
// 陷阱1:悬挂引用
std::function<int()> create_dangling_lambda() {
int local_var = 42;
return { return local_var; }; // 危险:返回局部变量的引用
}
// 解决:使用值捕获或确保生命周期
std::function<int()> create_safe_lambda() {
int local_var = 42;
return { return local_var; }; // 安全:值捕获
}
// 陷阱2:在 lambda 中捕获移动的变量
auto create_resource_handler() {
auto resource = std::make_unique<int>(42);
// 错误:捕获了即将被移动的变量
// return { return *resource; };
// 正确:使用移动捕获
return { return *r; };
}
// 陷阱3:在构造函数中使用 lambda
class Widget {
std::function<void()> callback_;
public:
Widget() {
// 避免在构造函数中捕获不完整的 this
int data = 42;
callback_ = { // 安全:不捕获 this
std::cout << "Data: " << data << std::endl;
};
}
};
}
8.3 现代 C++ 特性结合
与 constexpr 结合(C++17)
#include <iostream>
constexpr auto compile_time_lambda = int n {
return n * n;
};
int main() {
// 编译时计算
constexpr int result = compile_time_lambda(5);
std::cout << "Compile-time result: " << result << std::endl;
// 编译时 lambda 可以用于模板参数(C++20)
auto greater_than = []<typename T>(T threshold) {
return T value { return value > threshold; };
};
auto is_positive = greater_than(0);
std::cout << "5 is positive: " << is_positive(5) << std::endl;
return 0;
}
与概念(Concepts)结合(C++20)
#include <concepts>
#include <iostream>
// C++20 概念约束的 lambda
auto arithmetic_operation = []<std::integral T>(T a, T b) {
return a + b; // 只接受整数类型
};
// 使用概念约束可变参数 lambda
auto print_arithmetic = []<std::arithmetic... Ts>(Ts... args) {
(std::cout << ... << args) << std::endl;
};
int main() {
std::cout << arithmetic_operation(5, 3) << std::endl;
// arithmetic_operation(3.14, 2.71); // 错误:浮点数不满足 std::integral
print_arithmetic(1, 2.5, 3); // 正确:支持算术类型
return 0;
}
这份详细的 C++ Lambda 表达式教程涵盖了从基础语法到高级用法的所有核心内容,包括各种捕获方式、参数处理、返回类型、性能优化和最佳实践。Lambda 表达式是现代 C++ 编程中不可或缺的工具,合理使用可以显著提高代码的可读性和可维护性。
更多推荐



所有评论(0)