C++lambda表达式

C++ 的 lambda 表达式 是一种简洁的方式,用于定义匿名函数或可调用对象。它通常用于需要短小、内联定义的函数,尤其适用于 STL 算法、并行编程、事件处理等场景。Lambda 表达式的语法灵活且强大,可以捕获外部变量、指定参数类型、返回值类型等。

1. Lambda 表达式的基本语法:

[捕获列表](参数列表) -> 返回类型 { 函数体 }
  • 捕获列表 [ ]:指定 Lambda 表达式如何捕获外部变量。可以通过值捕获(复制外部变量)或者引用捕获(直接引用外部变量)等方式。
  • 参数列表 ( ):指定函数的输入参数。
  • 返回类型 ->:指定 Lambda 表达式的返回类型,通常情况下,C++ 可以自动推导返回类型,因此可以省略 -> 部分。如果需要,可以显式指定返回类型。
  • 函数体 { }:Lambda 表达式的代码块,执行实际的操作。

2. 捕获外部变量(捕获列表)

Lambda 表达式可以捕获外部作用域中的变量(即定义在 Lambda 外部的变量),这使得 Lambda 能够在局部环境中使用这些外部变量。

  • 按值捕获[x] 复制外部变量 x 的值到 Lambda 中。
  • 按引用捕获[&] 按引用捕获所有外部变量,使得 Lambda 内部能够修改外部变量。
  • 混合捕获:可以同时按值和按引用捕获不同的变量,例如 [x, &y] 表示按值捕获 x,按引用捕获 y
  • 捕获 this 指针[this] 可以捕获当前对象的 this 指针,用于在 Lambda 表达式中访问类的成员变量或成员函数。

3. 示例代码

3.1 基本 Lambda 表达式
#include <iostream>
using namespace std;

int main() {
    // 一个不带捕获的 Lambda,接受两个整数参数并返回它们的和
    auto add = [](int a, int b) -> int {
        return a + b;
    };

    cout << "Sum: " << add(3, 5) << endl;  // 输出: Sum: 8
    return 0;
}
3.2 捕获外部变量
#include <iostream>
using namespace std;

int main() {
    int x = 10, y = 20;
    
    // 按值捕获 x 和按引用捕获 y
    auto lambda = [x, &y]() {
        cout << "Captured by value: " << x << endl;  // x 的值是 10
        cout << "Captured by reference: " << y << endl;  // y 的值是 20
        y += 5;  // 修改 y 的值
    };

    lambda();
    cout << "After lambda, y = " << y << endl;  // 输出: After lambda, y = 25
    return 0;
}
  • 在上面的例子中,x 按值捕获,而 y 按引用捕获。调用 Lambda 后,y 的值被修改,而 x 的值保持不变。
3.3 捕获 this 指针(在类成员函数中使用)
#include <iostream>
using namespace std;

class MyClass {
public:
    MyClass(int val) : value(val) {}

    void print() {
        // 捕获 this 指针
        auto lambda = [this]() {
            cout << "Value from class: " << value << endl;
        };
        lambda();
    }

private:
    int value;
};

int main() {
    MyClass obj(42);
    obj.print();  // 输出: Value from class: 42
    return 0;
}
  • 在上面的代码中,Lambda 表达式通过捕获 this 指针访问类的成员变量 value。捕获 this 使得 Lambda 可以访问当前对象的成员。
3.4 返回类型推导与显式返回类型
  • 自动推导返回类型:Lambda 表达式可以省略返回类型,C++ 会自动推导。
  • 显式指定返回类型:可以通过 -> 语法显式指定 Lambda 的返回类型。
#include <iostream>
using namespace std;

int main() {
    // 自动推导返回类型
    auto multiply = [](int a, int b) {
        return a * b;
    };
    cout << "Product: " << multiply(3, 4) << endl;  // 输出: Product: 12

    // 显式指定返回类型
    auto divide = [](int a, int b) -> double {
        return static_cast<double>(a) / b;
    };
    cout << "Division: " << divide(5, 2) << endl;  // 输出: Division: 2.5

    return 0;
}
  • 第一个 multiply Lambda 没有显式返回类型,编译器根据 a * b 的类型自动推导出返回值为 int
  • 第二个 divide Lambda 显式指定了返回类型为 double,即使 a 和 b 是 int 类型,返回值类型也是 double

4. Lambda 表达式的应用场景

  • STL 算法:很多 STL 算法(如 std::for_each, std::sort, std::transform 等)都可以接受 Lambda 表达式作为参数,用于指定如何处理集合中的元素。

    示例:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    int main() {
        vector<int> vec = {1, 2, 3, 4, 5};
    
        // 使用 Lambda 表达式打印每个元素
        for_each(vec.begin(), vec.end(), [](int x) {
            cout << x << " ";
        });
    
        cout << endl;
        return 0;
    }
    

  • 事件处理和回调函数:Lambda 表达式可以用作回调函数(如 GUI 程序中的事件处理、并发编程中的线程回调等)。

  • 并行编程:在并行编程中,Lambda 使得任务划分和分配变得简单和直观。

    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    int main() {
        // 使用 Lambda 创建一个新线程
        thread t([]() {
            cout << "Hello from thread!" << endl;
        });
    
        t.join();  // 等待线程结束
        return 0;
    }
    

    Lambda是如何捕获的?

C++ 中,Lambda 捕获外部变量是基于作用域,而不是类型或最近距离。捕获变量是按照 作用域(即变量定义的范围)来决定的,而不是基于变量类型或物理位置的距离。

关键点:

  • Lambda 捕获变量是基于外部作用域中可访问的变量。
  • 如果捕获的变量与 Lambda 作用域内的其他变量同名,Lambda 会捕获 最外层作用域 中的变量,而不是局部作用域的同名变量。
  • Lambda 捕获是 静态的,即在编译时确定,而不是动态判断的。
#include <iostream>

void example() {
    int x = 10;  // 局部变量 x

    // Lambda 捕获外部变量 x
    auto lambda1 = [x]() {  // 捕获外部 x
        std::cout << "lambda1 captured x (by value): " << x << std::endl;
    };

    {
        int x = 20;  // 内部作用域中的局部变量 x

        // Lambda 捕获外部 x
        auto lambda2 = [x]() {  // 捕获外部 x
            std::cout << "lambda2 captured x (by value): " << x << std::endl;
        };

        lambda1();  // 输出 lambda1 捕获的外部作用域的 x
        lambda2();  // 输出 lambda2 捕获的内部作用域的 x
    }

    // 内部作用域结束,lambda2 销毁
    lambda1();  // 输出 lambda1 捕获的外部作用域的 x
}

int main() {
    example();
    return 0;
}
  1. lambda1 捕获的是函数外层作用域的 x(值为 10)。
  2. lambda2 在内层作用域中定义,它捕获的是内层作用域中的 x(值为 20)。尽管在 lambda2 定义时,外层作用域的 x 仍然是存在的,Lambda 会捕获其定义时作用域中的 x。这证明了捕获变量是基于作用域,而不是距离或类型。

进一步解释:

  • 作用域规则lambda1 捕获的是外层作用域中的 x(值 10),而 lambda2 捕获的是它定义时的内层作用域中的 x(值 20)。
  • 捕获规则:Lambda 在定义时,根据它的作用域来决定捕获什么变量。捕获的变量不是基于类型或物理距离,而是 基于编译时的作用域

如果外部作用域有多个变量,Lambda 可以通过捕获列表显式指定需要捕获哪些变量。捕获列表允许你选择性地捕获特定的变量,并且可以按值或按引用捕获。

#include <iostream>

void example() {
    int a = 10, b = 20;

    // 捕获 a 按值,b 按引用
    auto lambda = [a, &b]() {
        std::cout << "Captured a by value: " << a << std::endl;
        std::cout << "Captured b by reference: " << b << std::endl;
        b += 10;  // 修改 b
    };

    lambda();

    // 输出修改后的 b
    std::cout << "After lambda, b = " << b << std::endl;
}

int main() {
    example();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值