C++ 中的 lambda 表达式和 std::bind

1.Lambda 表达式:提供简单的方式定义匿名函数对象

1.2函数语法

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

1.3函数参数分析

1.3.1 [capture_clause]: 定义了 lambda 表达式从其所在作用域中捕获哪些变量以及如何捕获(值捕获或引用捕获)

        值捕获:创建变量的副本。

                1.lambda内部使用的是外部变量的拷贝

                2.外部变量的修改不会影响lambda内部的副本

                3.lambda内部对副本的修改不会影响外部变量

        引用捕获:使用变量的引用

                1.lambda内部直接操作外部变量

 捕获情况

[]:不捕获任何变量。                        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​[=]:通过值捕获所有外部变量。

[&]:通过引用捕获所有外部变量。​​​​​​​​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​            

[x, &y]:通过值捕获 x,通过引用捕获 y

[=, &z]:通过值捕获所有外部变量,但通过引用捕获 z。​​​​​​​ ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​ 

[&, a]:通过引用捕获所有外部变量,但通过值捕获 a

[this]:捕获所在的类对象的 this 指针,从而可以访问类成员。

1.3.2  (parameters): 和普通函数的参数列表一样(可选)

1.3.3  -> return_type: 可以省略,编译器会自动推导(可选)。但在某些复杂情况下需要显式指定。

// 完整语法
[capture] (parameters) -> return_type { body }

// 省略返回类型(编译器推导)
[capture] (parameters) { body }

// 省略参数(无参数)
[capture] { body }

// 省略捕获和参数(最简单的lambda)
[] { std::cout << "Hello" << std::endl; }

        可省略情况:当 lambda 函数体只有一个 return 语句,或者所有返回路径返回相同类型时,编译器可以自动推导返回类型

        

#include <iostream>

int main() {
    // 情况1:单一return语句 - 可以省略
    auto lambda1 = [](int a, int b) { // 省略 -> int
        return a + b; // 编译器推导为 int
    };
    
    // 情况2:多个return,但类型相同 - 可以省略
    auto lambda2 = [](int x) { // 省略 -> int
        if (x > 0) {
            return x * 2;
        } else {
            return x * 3; // 都是 int 类型
        }
    };
    
    // 情况3:void 返回类型 - 可以省略
    auto lambda3 = [](const std::string& msg) { // 省略 -> void
        std::cout << msg << std::endl;
        // 没有return语句,推导为void
    };
    
    std::cout << lambda1(5, 3) << std::endl; // 输出: 8
    std::cout << lambda2(5) << std::endl;    // 输出: 10
    lambda3("Hello");                        // 输出: Hello
    
    return 0;
}

       

         不可省略情况:当编译器无法自动推导返回类型时,必须显式指定。

#include <iostream>
#include <vector>

int main() {
    // 情况1:多个return语句返回不同类型 - 必须显式指定
    // auto ambiguous_lambda = [](bool flag) {
    //     if (flag) {
    //         return 10;    // int
    //     } else {
    //         return 3.14;  // double → 错误!类型不一致
    //     }
    // };
    
    // 正确:显式指定返回类型为 double
    auto good_lambda1 = [](bool flag) -> double {
        if (flag) {
            return 10;    // int 转换为 double
        } else {
            return 3.14;  // double
        }
    };
    
    // 情况2:递归lambda - 必须显式指定
    // auto factorial = [](int n) {
    //     if (n <= 1) return 1;
    //     return n * factorial(n - 1); // 错误!编译器还不知道返回类型
    // };
    
    // 正确:使用 std::function 或显式指定类型
    auto factorial = [](int n) -> int {
        if (n <= 1) return 1;
        return n * factorial(n - 1); // 现在可以了
    };
    
    std::cout << good_lambda1(true) << std::endl;  // 输出: 10
    std::cout << factorial(5) << std::endl;        // 输出: 120
    
    return 0;
}

1.3.4   { }:和普通函数一样。

1.4  常用使用例子

与 STL 算法结合(最常用)

#include <iostream>
#include <vector>
#include <algorithm> // for std::for_each, std::sort, etc.

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

    // 1. 使用 lambda 进行排序(按降序)
    std::sort(numbers.begin(), numbers.end(), [](int a, int b) {
        return a > b; // 降序规则
    });

    // 2. 使用 lambda 打印每个元素(for_each)
    std::for_each(numbers.begin(), numbers.end(), [](int n) {
        std::cout << n << " ";
    });
    std::cout << std::endl;

    // 3. 使用 lambda 计算有多少个大于 5 的数(count_if)
    int count = std::count_if(numbers.begin(), numbers.end(), [](int n) {
        return n > 5;
    });
    std::cout << "Count of numbers > 5: " << count << std::endl;

    // 4. 使用 lambda 查找第一个偶数(find_if)
    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;
    }

    return 0;
}

捕获局部变量

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

int main() {
    std::vector<int> scores = {85, 92, 78, 96, 88};
    int passing_score = 90;

    // 值捕获 passing_score
    auto it = std::find_if(scores.begin(), scores.end(),
                          [passing_score](int score) { // 将 passing_score 的值拷贝进来
                              return score >= passing_score;
                          });

    // 引用捕获,用于修改外部变量
    int count = 0;
    std::for_each(scores.begin(), scores.end(),
                 [&count](int score) { // 通过引用捕获 count,以便修改它
                     if (score > 90) count++;
                 });
    std::cout << "Number of scores above 90: " << count << std::endl;

    return 0;
}

在异步编程中的应用(如 std::thread, std::async)

#include <iostream>
#include <thread>
#include <future>

int main() {
    int x = 10;

    // 启动一个线程,lambda 捕获了 x 的引用
    std::thread t([&x]() {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        x *= 2; // 在线程中修改 x
        std::cout << "In thread, x = " << x << std::endl;
    });

    std::cout << "In main, x = " << x << std::endl;
    t.join(); // 等待线程结束
    std::cout << "After thread, x = " << x << std::endl; // x 已被修改

    // 与 std::async 结合,进行异步计算
    auto future = std::async(std::launch::async, [](int a, int b) {
        return a + b;
    }, 20, 30); // 参数 20 和 30 传递给 lambda

    int result = future.get(); // 获取异步结果
    std::cout << "Async result: " << result << std::endl;

    return 0;
}

作为回调函数

#include <iostream>
#include <functional>

// 一个模拟的按钮类
class Button {
public:
    // 设置点击事件回调函数
    void setOnClick(const std::function<void()>& callback) {
        onClick_ = callback;
    }

    void click() {
        if (onClick_) {
            onClick_();
        }
    }

private:
    std::function<void()> onClick_;
};

int main() {
    Button btn;
    std::string message = "Button clicked!";

    // 设置 lambda 作为回调,捕获了 message
    btn.setOnClick([&message]() {
        std::cout << message << std::endl;
    });

    // 模拟点击事件
    btn.click();

    return 0;
}

2.std::bind:将可调用对象(函数,函数指针,成员函数,lambda等)与其它参数进行绑定,生成一个新的可调用对象。

1.头文件和语法

#include <functional> // 必须包含这个头文件

auto new_callable = std::bind(existing_callable, arg_list);

2。函数参数

existing_callable:现有的可调用对象。

arg_list:参数列表。可以是占位符(如 std::placeholders::_1, _2, ...),也可以是具体的值

3.举例

绑定普通函数,并固定部分参数

#include <iostream>
#include <functional>

// 一个普通函数
void print_sum(int a, int b, int c) {
    std::cout << a << " + " << b << " + " << c << " = " << (a + b + c) << std::endl;
}

int main() {
    // 将 print_sum 的第三个参数固定为 100
    // _1 和 _2 是占位符,表示新可调用对象的第一和第二个参数
    auto bind_func1 = std::bind(print_sum, std::placeholders::_1, std::placeholders::_2, 100);

    // 调用 bind_func1(10, 20) 相当于调用 print_sum(10, 20, 100)
    bind_func1(10, 20);

    // 也可以重新排列参数顺序
    // 绑定为:第一个参数固定为 1,第二个参数是调用时的第一个参数,第三个参数是调用时的第二个参数
    auto bind_func2 = std::bind(print_sum, 1, std::placeholders::_2, std::placeholders::_1);
    // 调用 bind_func2(200, 300) 相当于调用 print_sum(1, 300, 200)
    bind_func2(200, 300);

    return 0;
}

绑定成员函数(非常重要)

成员函数有一个隐式的 this 指针作为第一个参数。

#include <iostream>
#include <functional>

class Calculator {
public:
    int multiply(int a, int b) {
        std::cout << "Calculating... ";
        return a * b;
    }
};

int main() {
    Calculator calc;

    // 绑定成员函数
    // 第一个参数必须是该类的对象(或指针/引用)
    // 方式1:使用对象
    auto bound_member = std::bind(&Calculator::multiply, calc, // 传入对象,会拷贝一份
                                  std::placeholders::_1,
                                  std::placeholders::_2);
    std::cout << bound_member(5, 6) << std::endl;

    // 方式2:使用对象引用(更常用,避免不必要的拷贝)
    auto bound_member_ref = std::bind(&Calculator::multiply, &calc, // 传入对象指针
                                      std::placeholders::_1,
                                      std::placeholders::_2);
    std::cout << bound_member_ref(7, 8) << std::endl;

    // 方式3:固定部分参数
    auto bound_member_fixed = std::bind(&Calculator::multiply, &calc,
                                        10, // 固定第一个参数为 10
                                        std::placeholders::_1); // 调用时只提供一个参数
    std::cout << bound_member_fixed(5) << std::endl; // 相当于 calc.multiply(10, 5)

    return 0;
}

绑定 lambda 表达式

既然 std::bind 接受任何可调用对象,自然也可以绑定 lambda。

#include <iostream>
#include <functional>

int main() {
    auto my_lambda = [](int a, const std::string& b, double c) {
        std::cout << a << ", " << b << ", " << c << std::endl;
    };

    // 绑定 lambda,并固定第二个和第三个参数
    auto bound_lambda = std::bind(my_lambda,
                                  std::placeholders::_1, // 新调用对象的第一个参数对应 lambda 的第一个参数
                                  "Hello",              // 固定 lambda 的第二个参数
                                  3.14);               // 固定 lambda 的第三个参数

    // 调用 bound_lambda(100) 相当于调用 my_lambda(100, "Hello", 3.14)
    bound_lambda(100);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值