C++ Lambda表达式复习

C++ Lambda表达式 (C++ Lambda Expressions: Beginner to Advanced)

Lambda表达式是C++11引入的一种轻量级匿名函数语法,支持闭包捕获,可以简化代码逻辑,特别是在函数式编程、回调函数和STL算法场景中尤为常用。本文将从基础语法到高级应用,逐步详解Lambda表达式的使用方法和内部机制。


1. 基础语法 (Basic Syntax)

Lambda表达式的完整形式如下:

[捕获列表] (参数列表) -> 返回类型 { 函数体 }
  • 捕获列表 (Capture List):指定Lambda中可以使用的外部变量。
  • 参数列表 (Parameter List):与普通函数类似的参数列表。
  • 返回类型 (Return Type):可选,省略时由编译器自动推导。
  • 函数体 (Function Body):Lambda的主要逻辑代码。

示例:

auto lambda = [](int x, int y) -> int {
    return x + y;
};
std::cout << lambda(3, 4); // 输出: 7

2. 捕获列表详解 (Capture List Details)

捕获列表的作用是指定Lambda表达式中如何访问外部作用域变量。捕获可以按值或按引用进行,也可以混合使用。

捕获方式 (Capture Modes)

  1. 按值捕获(By Value)

    • 捕获变量的值副本,Lambda内部的修改不会影响原变量。
    • 使用[x]捕获单个变量,或[=]捕获所有变量。
    int a = 10;
    auto lambda = [a]() {
        std::cout << a << std::endl; // 输出: 10
    };
    a = 20;
    lambda(); // 输出仍然是10,因为捕获的是a的副本
    
  2. 按引用捕获(By Reference)

    • 捕获变量的引用,Lambda内部的修改会影响原变量。
    • 使用[&x]捕获单个变量,或[&]捕获所有变量。
    int b = 20;
    auto lambda = [&b]() {
        b += 10;
        std::cout << b << std::endl; // 修改b并输出: 30
    };
    lambda();
    std::cout << b << std::endl; // 外部的b变为: 30
    
  3. 混合捕获(Mixed Capture)

    • 可以同时使用按值捕获和按引用捕获。
    • 示例:[=, &y]表示默认按值捕获,变量y按引用捕获。
    int x = 5, y = 10;
    auto lambda = [=, &y]() {
        // x是按值捕获,y是按引用捕获
        std::cout << "x: " << x << ", y: " << y << std::endl;
        y++;
    };
    lambda();
    std::cout << "y: " << y << std::endl; // y被修改为: 11
    
  4. 捕获this指针

    • [this]捕获当前对象的指针,允许访问成员变量和成员函数。
    • [=, this]:按值捕获外部变量并捕获this
    class Foo {
    public:
        int value = 42;
        auto getLambda() {
            return [this]() {
                std::cout << value << std::endl;
            };
        }
    };
    Foo f;
    auto lambda = f.getLambda();
    lambda(); // 输出: 42
    
  5. 初始化捕获(C++14起)

    • 捕获列表支持直接初始化变量(即实现“捕获时初始化”)。
    • 示例:[z = a + 1]
    int a = 10;
    auto lambda = [z = a + 1]() {
        std::cout << z << std::endl; // 输出: 11
    };
    lambda();
    

3. 参数列表与返回类型 (Parameter List & Return Type)

参数列表 (Parameter List)

Lambda表达式的参数列表与普通函数类似,但可以为任意类型。

auto add = [](int x, double y) {
    return x + y;
};
std::cout << add(3, 4.5); // 输出: 7.5

返回类型 (Return Type)

  • 返回类型可以由编译器自动推导,通常无需显式声明。
  • 如果需要显式指定返回类型,可以使用->
auto divide = [](int x, int y) -> double {
    return static_cast<double>(x) / y;
};
std::cout << divide(10, 4); // 输出: 2.5

4. mutable关键字 (mutable Keyword)

默认情况下,按值捕获的变量在Lambda内部是只读的。如果需要修改这些值,可以使用mutable关键字。

int count = 5;
auto lambda = [count]() mutable {
    count++;
    std::cout << count << std::endl; // 输出: 6
};
lambda();
std::cout << count << std::endl; // 外部的count仍然是: 5

5. STL中的应用 (Using Lambda with STL)

Lambda表达式在STL中非常实用,常用于算法如std::for_eachstd::find_ifstd::sort等。

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};

    // 使用Lambda打印所有元素
    std::for_each(nums.begin(), nums.end(), [](int num) {
        std::cout << num << " ";
    });
    std::cout << std::endl;

    // 使用Lambda查找第一个大于3的元素
    auto it = std::find_if(nums.begin(), nums.end(), [](int num) {
        return num > 3;
    });
    if (it != nums.end()) {
        std::cout << "First number > 3: " << *it << std::endl;
    }

    // 使用Lambda对元素排序(降序)
    std::sort(nums.begin(), nums.end(), [](int a, int b) {
        return a > b;
    });
    for (int x : nums) std::cout << x << " "; // 输出: 5 4 3 2 1

    return 0;
}

6. 泛型Lambda (Generic Lambda, C++14起)

C++14引入泛型Lambda,支持auto作为参数类型占位符。

auto add = [](auto a, auto b) {
    return a + b;
};
std::cout << add(3, 4) << std::endl;       // 输出: 7
std::cout << add(1.5, 2.5) << std::endl;   // 输出: 4

7. Lambda递归 (Recursive Lambda)

由于Lambda表达式是匿名的,默认情况下无法直接递归调用。如果需要递归,可以使用std::function

#include <functional>
#include <iostream>

std::function<int(int)> factorial = [&](int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
};

int main() {
    std::cout << "Factorial of 5: " << factorial(5) << std::endl; // 输出: 120
    return 0;
}

8. constexpr Lambda (C++17起)

C++17起,Lambda表达式可以被声明为constexpr,支持编译期求值。

constexpr auto square = [](int x) {
    return x * x;
};
constexpr int result = square(5);
static_assert(result == 25, "Compile-time evaluation failed");

9. 捕获移动对象 (Capturing Move-Only Objects, C++14起)

从C++14开始,Lambda支持移动捕获,允许捕获独占资源(如智能指针)。

#include <memory>
#include <iostream>

int main() {
    auto ptr = std::make_unique<int>(42);

    auto lambda = [p = std::move(ptr)]() {
        std::cout << *p << std::endl; // 输出: 42
    };

    lambda();
    return 0;
}

10. Lambda的类型与std::function (Lambda Type and std::function)

Lambda表达式的类型是匿名的,无法直接使用。如果需要存储到一个变量中并保持灵活性,可以使用std::function

#include <functional>

std::function<int(int)> increment = [](int x) {
    return x + 1;
};
std::cout << increment(5) << std::endl; // 输出: 6

11. 注意事项与最佳实践 (Pitfalls & Best Practices)

  1. 悬垂引用(Dangling References)

    • 捕获引用时要确保被引用的变量生命周期足够长,避免悬垂引用。
    int* ptr = new int(10);
    auto lambda = [&]() { *ptr = 20; };
    delete ptr; // 悬垂指针,lambda调用后可能导致未定义行为
    
  2. 按值捕获的副本

    • 捕获的是变量的当前值,而非引用。
    int x = 10;
    auto lambda = [x]() {
        std::cout << x << std::endl;
    };
    x = 20;
    lambda(); // 输出: 10
    
  3. 复杂逻辑与可读性

    • Lambda过于复杂时,建议改用普通函数,以提高代码可读性。

12. 总结与应用场景 (Summary and Use Cases)

Lambda表达式是C++现代编程的重要特性,其应用场景包括:

  • STL算法:简化传递行为参数。
  • 回调函数:处理异步任务或事件驱动。
  • 函数组合:实现函数式编程的思想。
  • 多线程:结合std::threadstd::async

C++ Lambda表达式题目


多选题 (Multiple Choice Questions)

1. 关于C++的Lambda表达式,以下哪些说法正确?(多选)

A. 使用[&]捕获时,Lambda会按引用捕获所有变量。
B. Lambda表达式可以传递给STL算法作为参数。
C. Lambda表达式的类型是一个匿名类型,无法直接声明变量。
D. 在Lambda中修改按值捕获的变量需要加mutable关键字。


2. 以下代码的输出结果是什么?(多选)

int a = 10, b = 20;
auto lambda = [=]() mutable {
    a++;
    b++;
    std::cout << a << " " << b << std::endl;
};
lambda();
std::cout << a << " " << b << std::endl;

A. 11 21
B. 10 20
C. 10 21
D. 11 20


3. 以下代码中,哪些捕获方式是合法的?(多选)

int x = 10, y = 20;
auto lambda1 = [x, &y]() {};
auto lambda2 = [&x, y]() {};
auto lambda3 = [=, &x]() {};
auto lambda4 = [&, x]() {};

A. lambda1
B. lambda2
C. lambda3
D. lambda4


4. 以下代码是否可以正确编译?如果不能,选择原因。(多选)

#include <memory>
auto ptr = std::make_unique<int>(42);
auto lambda = [p = std::move(ptr)]() {
    std::cout << *p << std::endl;
};
lambda();

A. 可以编译,输出42
B. 捕获列表中不能使用std::move
C. 捕获的p生命周期错误
D. ptr已经被移动,后续访问会出错


5. 如何在Lambda中实现递归调用?(多选)

A. 使用std::function包装Lambda
B. 直接在Lambda中调用自身
C. 捕获Lambda的引用
D. Lambda递归只能通过外部普通函数实现


6. 以下哪些Lambda表达式是constexpr的?(多选)

auto lambda1 = [](int x) { return x * x; };
constexpr auto lambda2 = [](int x) { return x + x; };
auto lambda3 = [](int x) constexpr { return x - 1; };
auto lambda4 = []() { return 42; };

A. lambda1
B. lambda2
C. lambda3
D. lambda4


7. 以下代码的输出结果是什么?(多选)

int x = 10;
auto lambda = [x = x + 1]() {
    std::cout << x << std::endl;
};
x = 20;
lambda();

A. 10
B. 11
C. 20
D. 编译错误


8. 以下哪些场景适合使用Lambda表达式?(多选)

A. STL算法中的回调逻辑
B. 实现高性能递归函数
C. 并发编程中的线程入口函数
D. 定义大规模复杂的业务逻辑


9. 关于泛型Lambda,以下说法正确的是?(多选)

A. 泛型Lambda支持auto作为参数类型
B. 泛型Lambda只能用于返回值是void的函数
C. 泛型Lambda在C++14中引入
D. 泛型Lambda不能与其他捕获列表同时使用


10. 以下代码的输出结果是什么?(多选)

#include <vector>
#include <algorithm>
int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto it = std::find_if(vec.begin(), vec.end(), [](int x) {
        return x > 3;
    });
    if (it != vec.end()) {
        std::cout << *it << std::endl;
    }
    return 0;
}

A. 3
B. 4
C. 5
D. 编译错误


设计题 (Design Questions)

1. 使用Lambda表达式实现一个通用的排序函数,支持升序和降序,并能够接受自定义比较器。


2. 设计一个简单的事件驱动系统,使用Lambda表达式注册事件回调并触发事件。


3. 使用Lambda表达式实现一个轻量级的任务调度器,能够按照优先级执行任务。


4. 使用Lambda表达式和std::function实现一个可以递归计算斐波那契数列的函数,并支持自定义初始值。


5. 使用Lambda表达式设计一个简单的资源管理类,能够自动释放资源(如文件或内存),支持自定义释放逻辑。


答案与详解 (Answers and Explanations)

多选题答案

  1. 正确答案:A, B, C, D

    • A: [&]按引用捕获所有外部变量。
    • B: Lambda可以作为函数对象传递给STL算法。
    • C: Lambda的类型是匿名的,只能通过autostd::function存储。
    • D: 按值捕获的变量是只读的,使用mutable可以修改其副本。
  2. 正确答案:A, B

    • Lambda内部ab是按值捕获的副本,修改不会影响外部变量。
    • 输出分别是11 21(Lambda内部)和10 20(外部)。
  3. 正确答案:A, B, D

    • A: 按值捕获x和按引用捕获y是合法的。
    • B: 合法,因为引用捕获x、按值捕获y
    • C: [=, &x]是非法的,不能混用=和具体按引用捕获。
    • D: 合法,[&, x]允许按值捕获x
  4. 正确答案:A

    • 代码合法,运行时输出42,因为std::move捕获将所有权转移到Lambda内部。
  5. 正确答案:A, C

    • A: 使用std::function封装Lambda支持递归。
    • C: 捕获Lambda的引用也可以实现递归。
  6. 正确答案:B, C

    • B: constexpr修饰Lambda表达式。
    • C: Lambda内部声明为constexpr
  7. 正确答案:B

    • 捕获列表x = x + 1初始化了x11,后续修改外部x不影响。
  8. 正确答案:A, C

    • A: Lambda常用于STL算法,如std::for_each
    • C: 并发编程中,Lambda可作为线程入口。
  9. 正确答案:A, C

    • A: 泛型Lambda支持auto作为参数类型。
    • C: 泛型Lambda在C++14中引入。
  10. 正确答案:B

  • Lambda表达式找到第一个大于3的元素,输出4

设计题详解

1. 通用排序函数
#include <vector>
#include <algorithm>

template<typename T, typename Compare = std::less<T>>
void sort(std::vector<T>& vec, Compare comp = Compare()) {
    std::sort(vec.begin(), vec.end(), comp);
}

int main() {
    std::vector<int> vec = {5, 2, 8, 1};
    sort(vec, [](int a, int b) { return a > b; }); // 降序
    return 0;
}
2. 事件驱动系统
#include <functional>
#include <unordered_map>
#include <vector>

class EventSystem {
public:
    using Callback = std::function<void()>;
    void registerEvent(const std::string& eventName, Callback callback) {
        events[eventName].push_back(callback);
    }
    void trigger(const std::string& eventName) {
        for (auto& callback : events[eventName]) {
            callback();
        }
    }
private:
    std::unordered_map<std::string, std::vector<Callback>> events;
};
3. 任务调度器
#include <queue>
#include <functional>

class TaskScheduler {
public:
    using Task = std::function<void()>;
    void addTask(Task task, int priority) {
        tasks.emplace(priority, task);
    }
    void run() {
        while (!tasks.empty()) {
            auto [priority, task] = tasks.top();
            task();
            tasks.pop();
        }
    }
private:
    std::priority_queue<std::pair<int, Task>> tasks;
};
4. 递归计算斐波那契数列
#include <functional>
#include <iostream>

std::function<int(int)> createFibonacci(int a = 0, int b = 1) {
    return [=](int n) mutable {
        if (n == 0) return a;
        if (n == 1) return b;
        return createFibonacci(b, a + b)(n - 1);
    };
}
5. 资源管理类
#include <functional>
#include <iostream>

class ResourceManager {
public:
    ResourceManager(std::function<void()> release) : releaseFunc(release) {}
    ~ResourceManager() { releaseFunc(); }
private:
    std::function<void()> releaseFunc;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值