C++ Lambda 表达式中的参数引用

Lambda 表达式是 C++11 引入的强大特性,它允许我们定义匿名函数对象。在 lambda 表达式中,参数的引用方式与普通函数类似,但有一些额外的捕获机制需要注意。

1. Lambda 表达式的基本语法

[capture](parameters) -> return_type { 
    // 函数体 
}

2. 参数传递方式

值传递参数

auto lambda = [](int x, int y) {
    return x + y;
};

int result = lambda(3, 4); // result = 7

引用传递参数

auto modifyValue = [](int& x) {
    x *= 2;
};

int value = 5;
modifyValue(value); // value 现在为 10

const 引用传递参数

auto printLargeObject = [](const std::vector<int>& vec) {
    for (const auto& item : vec) {
        std::cout << item << " ";
    }
    std::cout << std::endl;
};

std::vector<int> numbers = {1, 2, 3, 4, 5};
printLargeObject(numbers); // 不会复制整个vector

3. 捕获列表中的引用

Lambda 表达式可以通过捕获列表捕获外部变量,这是与普通函数不同的地方:

按值捕获

int x = 10;
auto lambda = [x]() { // 按值捕获x
    std::cout << x << std::endl;
    // x++; // 错误:按值捕获的变量默认是const的
};
lambda(); // 输出 10

按引用捕获

int x = 10;
auto lambda = [&x]() { // 按引用捕获x
    std::cout << x << std::endl;
    x++; // 可以修改,会影响外部变量
};
lambda(); // 输出 10
std::cout << x << std::endl; // 输出 11

使用 mutable 关键字

int x = 10;
auto lambda = [x]() mutable { // 按值捕获,但允许修改副本
    std::cout << x << std::endl;
    x++; // 修改的是副本,不影响外部变量
    std::cout << x << std::endl;
};
lambda(); // 输出 10, 然后 11
std::cout << x << std::endl; // 输出 10(外部变量未改变)

4. 通用引用和完美转发

C++14 引入了初始化捕获,可以更灵活地处理参数:

// C++14 通用 lambda
auto lambda = [](auto&& x) {
    return std::forward<decltype(x)>(x);
};

int a = 5;
const int b = 10;

auto result1 = lambda(a); // 传递左值
auto result2 = lambda(42); // 传递右值
auto result3 = lambda(b); // 传递const左值

5. 捕获 this 指针

在类成员函数中,lambda 可以捕获 this 指针来访问类成员:

class MyClass {
public:
    int value = 42;
    
    void printValue() {
        auto lambda = [this]() {
            std::cout << value << std::endl; // 通过this访问成员
        };
        lambda();
    }
};

6. 默认捕获模式

int x = 10, y = 20;

// 默认按值捕获所有外部变量
auto lambda1 = [=]() {
    std::cout << x + y << std::endl;
};

// 默认按引用捕获所有外部变量
auto lambda2 = [&]() {
    x++; y++;
    std::cout << x + y << std::endl;
};

// 混合捕获:x按值,其余按引用
auto lambda3 = [=, &y]() {
    // x是按值捕获,不能修改
    y++; // y是按引用捕获,可以修改
    std::cout << x + y << std::endl;
};

// 混合捕获:y按引用,其余按值
auto lambda4 = [&, x]() {
    // x是按值捕获,不能修改
    y++; // y是按引用捕获,可以修改
    std::cout << x + y << std::endl;
};

7. 实际应用示例

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

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    int threshold = 3;
    
    // 使用lambda作为谓词,捕获外部变量threshold
    auto it = std::find_if(numbers.begin(), numbers.end(),
        [threshold](int value) {
            return value > threshold;
        });
    
    if (it != numbers.end()) {
        std::cout << "First value greater than " << threshold 
                  << " is " << *it << std::endl;
    }
    
    // 修改vector中的元素
    std::for_each(numbers.begin(), numbers.end(),
        [](int& value) { // 参数为引用,可以修改元素
            value *= 2;
        });
    
    // 输出结果
    for (const auto& num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

注意事项

  1. 生命周期问题:按引用捕获变量时,要确保lambda被执行时,被捕获的变量仍然存在
  2. 悬空引用:如果捕获的引用指向的对象已被销毁,会导致未定义行为
  3. 默认捕获的风险:使用 [&][=] 可能会意外捕获不需要的变量
  4. mutable 的使用:只有需要修改按值捕获的变量时才使用mutable关键字

Lambda 表达式中的参数引用机制非常灵活,正确使用可以编写出简洁高效的代码,但需要注意避免常见的陷阱。

posted @ 2025-08-28 09:07  xhubobo  阅读(37)  评论(0)    收藏  举报