<C++&C#> lambda表达式

lambda表达式

C++

C++中的lambda表达式是C++11引入的重要特性,它允许在代码中定义匿名函数对象,极大地增强了函数式编程能力。

基本语法

cpp 复制代码
[capture] (parameters) -> return_type { body }
  • capture:捕获列表,指定lambda表达式可以访问的外部变量
  • parameters:参数列表,与普通函数的参数列表类似
  • return_type:返回类型(可省略,编译器会自动推导)
  • body:函数体,包含要执行的代码

捕获列表详解

捕获列表决定了lambda表达式如何访问外部作用域的变量:

cpp 复制代码
// 1. 值捕获(拷贝)
int x = 10;
auto lambda1 = [x] { return x + 5; };  // 捕获x的副本

// 2. 引用捕获
int y = 20;
auto lambda2 = [&y] { y += 5; return y; };  // 捕获y的引用

// 3. 隐式值捕获
auto lambda3 = [=] { return x + y; };  // 按值捕获所有外部变量

// 4. 隐式引用捕获
auto lambda4 = [&] { x++; y++; };  // 按引用捕获所有外部变量

// 5. 混合捕获
auto lambda5 = [=, &y] { return x + y; };  // x按值,y按引用
auto lambda6 = [&, x] { return x + y; };   // x按值,其他按引用

参数与返回类型

cpp 复制代码
// 完整形式
auto add = [](int a, int b) -> int {
    return a + b;
};

// 省略返回类型(编译器自动推导)
auto multiply = [](int a, int b) {
    return a * b;
};

// 无参数lambda
auto getAnswer = [] {
    return 42;
};

// 可变lambda(可修改按值捕获的变量)
int counter = 0;
auto increment = [counter]() mutable {
    return ++counter;  // 修改捕获的副本
};

实际应用示例

1. STL算法中的lambda
cpp 复制代码
#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 使用lambda过滤偶数
    auto it = std::remove_if(numbers.begin(), numbers.end(),
        [](int n) { return n % 2 != 0; });
    numbers.erase(it, numbers.end());
    
    // 使用lambda转换元素
    std::transform(numbers.begin(), numbers.end(), numbers.begin(),
        [](int n) { return n * 2; });
    
    // 使用lambda排序
    std::sort(numbers.begin(), numbers.end(),
        [](int a, int b) { return a > b; });  // 降序排序
    
    // 使用lambda查找
    auto result = std::find_if(numbers.begin(), numbers.end(),
        [](int n) { return n > 15; });
    
    return 0;
}
2. 作为函数参数
cpp 复制代码
#include <functional>
#include <iostream>

void processNumbers(const std::vector<int>& nums, 
                   std::function<void(int)> processor) {
    for (int num : nums) {
        processor(num);
    }
}

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    
    // 传递lambda作为回调函数
    processNumbers(numbers, [](int n) {
        std::cout << "Processing: " << n * 2 << std::endl;
    });
    
    // 带状态的lambda
    int sum = 0;
    processNumbers(numbers, [&sum](int n) {
        sum += n;
    });
    std::cout << "Total sum: " << sum << std::endl;
    
    return 0;
}
3. 延迟执行与函数对象
cpp 复制代码
#include <iostream>
#include <memory>

class TaskScheduler {
public:
    void schedule(std::function<void()> task) {
        // 存储任务供稍后执行
        tasks_.push_back(task);
    }
    
    void executeAll() {
        for (auto& task : tasks_) {
            task();
        }
    }
    
private:
    std::vector<std::function<void()>> tasks_;
};

int main() {
    TaskScheduler scheduler;
    
    // 创建多个lambda任务
    for (int i = 0; i < 5; ++i) {
        scheduler.scheduler([i]() {
            std::cout << "Task " << i << " executed" << std::endl;
        });
    }
    
    // 稍后执行所有任务
    scheduler.executeAll();
    
    return 0;
}

通用lambda(C++14+)

C++14引入了通用lambda,支持auto参数:

cpp 复制代码
// 通用lambda,支持任意类型参数
auto printPair = [](const auto& a, const auto& b) {
    std::cout << a << ", " << b << std::endl;
};

printPair(1, 2);              // int, int
printPair(3.14, "Hello");     // double, const char*
printPair(std::string("C++"), true);  // string, bool

// 在模板算法中使用
template<typename Container, typename Func>
void processContainer(Container& c, Func f) {
    for (auto& item : c) {
        f(item);
    }
}

std::vector<int> nums = {1, 2, 3};
processContainer(nums, [](auto x) {
    std::cout << x * 2 << " ";
});

初始化捕获(C++14+)

C++14允许在捕获列表中初始化变量:

cpp 复制代码
// 移动语义与lambda
auto ptr = std::make_unique<int>(42);
auto lambda = [p = std::move(ptr)]() {
    return *p;
};

// 在lambda内部创建新变量
auto createMultiplier = [factor = 2](int x) {
    return x * factor;
};

std::cout << createMultiplier(5) << std::endl;  // 输出10

注意事项

  1. 生命周期管理:引用捕获要注意被引用对象的生命周期
  2. 性能考虑:小lambda通常被编译器内联,性能接近普通函数
  3. mutable关键字:按值捕获的变量默认是const,需要mutable才能修改
  4. 类型推导:复杂返回类型可能需要显式指定
  5. 与std::function的转换:lambda可隐式转换为std::function,但可能有性能开销

C++20增强

C++20为lambda带来了更多特性:

  • 模板lambda参数
  • 可构造和可赋值的无状态lambda
  • 在未求值上下文中使用lambda
  • 捕获结构化绑定
cpp 复制代码
// C++20: 模板lambda
auto genericLambda = []<typename T>(T a, T b) {
    return a + b;
};

// C++20: 捕获结构化绑定
auto [x, y] = std::pair{1, 2};
auto lambda = [=] {
    return x + y;  // 正确捕获结构化绑定
};

C++的lambda表达式是现代C++编程的核心特性之一,它使得代码更加简洁、表达力更强,特别是在函数式编程和并发编程中发挥着重要作用。

C#

C#中的lambda表达式是匿名函数的一种简洁写法,主要用于创建委托或表达式树类型。

基本语法

csharp 复制代码
(parameters) => expression

或者

csharp 复制代码
(parameters) => { statements; }

示例

csharp 复制代码
// 1. 简单的lambda表达式
Func<int, int> square = x => x * x;
Console.WriteLine(square(5)); // 输出: 25

// 2. 带多个参数的lambda表达式
Func<int, int, int> add = (x, y) => x + y;
Console.WriteLine(add(3, 4)); // 输出: 7

// 3. 带语句块的lambda表达式
Func<int, int> factorial = n => {
    int result = 1;
    for (int i = 1; i <= n; i++)
        result *= i;
    return result;
};
Console.WriteLine(factorial(5)); // 输出: 120

// 4. 无参数的lambda表达式
Func<string> getMessage = () => "Hello, World!";
Console.WriteLine(getMessage()); // 输出: Hello, World!

// 5. 使用LINQ的lambda表达式
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(n => n % 2 == 0);
foreach (var num in evenNumbers)
    Console.WriteLine(num); // 输出: 2, 4

捕获外部变量

C#的lambda表达式可以捕获外部变量,这称为闭包:

csharp 复制代码
int multiplier = 3;
Func<int, int> multiply = x => x * multiplier;
Console.WriteLine(multiply(5)); // 输出: 15

// 修改捕获的变量
multiplier = 4;
Console.WriteLine(multiply(5)); // 输出: 20

与C++ lambda的对比

特性 C++ C#
语法 [capture](params) -> ret { body } (params) => expression
捕获方式 显式捕获列表 [&][=][var] 隐式捕获外部变量
返回类型 可显式指定或自动推导 自动推导
主要用途 函数对象、算法参数 委托、LINQ、事件处理
类型 函数对象 委托或表达式树

实际应用场景

  1. LINQ查询
csharp 复制代码
var students = new List<Student>();
var topStudents = students.Where(s => s.Score > 90)
                          .OrderByDescending(s => s.Score)
                          .Select(s => s.Name);
  1. 事件处理
csharp 复制代码
button.Click += (sender, e) => {
    MessageBox.Show("Button clicked!");
};
  1. 异步编程
csharp 复制代码
Task.Run(() => {
    // 异步执行的代码
    Console.WriteLine("Running in background");
});
  1. 表达式树
csharp 复制代码
Expression<Func<int, bool>> expr = x => x > 5;

注意事项

  1. 变量捕获的生命周期:捕获的变量在lambda表达式被调用时仍然需要存在
  2. 性能考虑:频繁创建lambda可能产生垃圾回收压力
  3. 可读性:复杂的lambda表达式应拆分为命名方法以提高可读性
  4. 调试:lambda表达式在调试时可能不如命名方法直观
相关推荐
咖啡八杯1 小时前
GoF设计模式——外观模式
java·设计模式·外观模式
郝学胜-神的一滴1 小时前
系统设计 014:缓存深度实战:如何用 Cache 优雅优化数据库读写?
java·数据库·python·缓存·oracle·php·软件构建
晚风叙码1 小时前
C++类和对象(中)| 深挖四大默认成员函数:构造/析构/拷贝/赋值重载原理全解
c++
xuankuxiaoyao1 小时前
阶段案例——后台管理系统
java·linux·前端
摇滚侠1 小时前
JavaWeb 全套教程 Tomcat 53-62
java·tomcat
混迹中的咸鱼1 小时前
游戏开发核心架构指南
c++·游戏·架构
隔窗听雨眠2 小时前
ORM框架选型指南:MyBatis与Hibernate的全面对比
java·开发语言·数据库
-凌凌漆-2 小时前
【Qt】C++中protected与private的区别
开发语言·c++·qt
j7~2 小时前
【C++】类和对象(上)--带你全面理解类和对象的概念,以及this指针的理解和相关面试题
java·开发语言·封装·this指针·类的实例化·访问限定符·类的命名