第六章QT基础: Lambda表达式补充

什么是 Lambda 表达式?

Lambda 表达式(匿名函数)是一种可以在不定义函数名称的情况下直接定义并使用的小型函数。在 C++11 及更高版本中,Lambda 表达式使得编写临时函数变得更加简洁和直观。

Lambda 表达式通常用于:

  • 传递函数作为参数。
  • 在需要临时功能时避免定义额外的函数。

Lambda 表达式的基本语法

Lambda 表达式的基本语法格式如下:

[捕获列表] (参数列表) -> 返回类型 { 函数体 }

组成部分:

  1. 捕获列表 (Capture List):
    捕获列表用于指定外部变量如何在 Lambda 中使用。可以捕获外部作用域的变量,并决定是按值捕获还是按引用捕获。

    • [=]:按值捕获外部作用域中的所有变量。

    • [&]:按引用捕获外部作用域中的所有变量。

    • [var]:按值捕获指定的变量 var

    • [&var]:按引用捕获指定的变量 var

    • [=, &var]:按值捕获所有外部变量,并按引用捕获指定变量 var

  2. 参数列表 (Parameter List):
    Lambda 表达式接受的参数列表,和普通函数一样。

  3. 返回类型 (Return Type):
    -> 返回类型 用于显式声明 Lambda 表达式的返回类型。C++11 之后可以省略返回类型,编译器会自动推断。

  4. 函数体 (Body):
    函数体是 Lambda 表达式的执行逻辑,类似普通函数的实现部分。

Lambda 表达式的捕获列表详细说明

在 C++ 中,捕获列表 是用于指定如何将外部变量传递到 Lambda 表达式中的一部分。捕获列表可以选择按值(=)或按引用(&)来捕获变量,也可以只捕获特定的变量。接下来,我们将逐一讲解您提到的几种捕获方式。

1. [=]:按值捕获外部作用域中的所有变量

[=] 语法表示按值捕获所有外部变量。Lambda 表达式会创建外部变量的副本,并在 Lambda 内部使用这些副本。

  • 副本当 Lambda 表达式调用时它使用的是外部变量的副本,而不是外部原始变量的地址。因此,对 Lambda 内部捕获变量的修改不会影响外部的原始变量。
#include <iostream>

int main() {
    int x = 10;
    int y = 20;

    // 按值捕获外部变量
    auto print_values = [=]() { 
        std::cout << "x: " << x << ", y: " << y << std::endl;
    };

    x = 15;  // 修改外部 x
    y = 25;  // 修改外部 y

    print_values();  // 输出:x: 10, y: 20(因为按值捕获,使用的是 x 和 y 的副本)
    return 0;
}
  • 输出
    x: 10, y: 20
    

解释

  • 即使在 print_values 调用时,xy 已经被修改,Lambda 仍然使用它们的原始值(10 和 20),因为它们在 Lambda 捕获时是按值捕获的。

2. [&]:按引用捕获外部作用域中的所有变量

[&] 语法表示按引用捕获所有外部变量。Lambda 表达式将直接使用外部变量的引用。因此,对 Lambda 内部捕获变量的修改会影响外部原始变量。

#include <iostream>

int main() {
    int x = 10;
    int y = 20;

    // 按引用捕获外部变量
    auto modify_values = [&]() { 
        x = 30;  // 修改外部 x
        y = 40;  // 修改外部 y
    };

    modify_values();  // 调用 Lambda,修改外部变量

    std::cout << "x: " << x << ", y: " << y << std::endl;  // 输出:x: 30, y: 40
    return 0;
}
  • 输出
    x: 30, y: 40
    

解释

  • Lambda 捕获外部变量 xy 时是按引用的,意味着修改 Lambda 内部的变量会直接影响外部变量 xy

3. [var]:按值捕获指定的变量 var

[var] 语法表示只按值捕获指定的变量 var。仅捕获指定的变量并创建该变量的副本,其他变量则不会被捕获。

#include <iostream>

int main() {
    int x = 10;
    int y = 20;

    // 只按值捕获变量 x
    auto print_value = [x]() { 
        std::cout << "x: " << x << std::endl;
    };
    x = 15;  // 修改外部 x
    print_value();  // 输出:x: 10(因为按值捕获,Lambda 使用的是 x 的副本)
    return 0;
}
  • 输出

    x: 10
    

解释

  • 由于 x 是按值捕获的,所以即使在 Lambda 调用时修改了 x,Lambda 使用的仍然是 x 的副本。

4. [&var]:按引用捕获指定的变量 var

[&var] 语法表示只按引用捕获指定的变量 var,意味着 var 是通过引用捕获的,其他变量则不被捕获。

#include <iostream>

int main() {
    int x = 10;
    int y = 20;

    // 只按引用捕获变量 y
    auto modify_y = [&y]() { 
        y = 30;  // 修改 y
    };

    modify_y();  // 调用 Lambda,修改外部变量 y

    std::cout << "y: " << y << std::endl;  // 输出:y: 30
    return 0;
}
  • 输出
    y: 30
    

解释

  • y 是按引用捕获的,因此 Lambda 内部对 y 的修改直接影响外部的 y。但 x 没有被捕获,因此它没有受到影响。

5. [=, &var]:按值捕获所有外部变量,并按引用捕获指定变量 var

[=, &var] 语法表示按值捕获所有外部变量,并按引用捕获特定变量 var。所有未显式列出的变量都会按值捕获,而 var 会按引用捕获。

#include <iostream>

int main() {
    int x = 10;
    int y = 20;

    // 按值捕获所有外部变量,按引用捕获 y
    auto modify_values = [=, &y]() { 
        std::cout << "x: " << x << ", y: " << y << std::endl;
        y = 50;  // 修改 y
    };

    modify_values();  // 修改 y

    std::cout << "x: " << x << ", y: " << y << std::endl;  // 输出:x: 10, y: 50
    return 0;
}
  • 输出

    x: 10, y: 20
    x: 10, y: 50
    

解释

  • x 是按值捕获的,因此 Lambda 使用的是 x 的副本,外部 x 的值不会改变。

  • y 是按引用捕获的,因此在 Lambda 内部修改 y 时,外部的 y 也会被修改。

总结

  1. [=]:按值捕获所有外部变量,变量的副本会传递给 Lambda,修改不会影响外部变量。

  2. [&]:按引用捕获所有外部变量,Lambda 内部的修改会直接影响外部变量。

  3. [var]:按值捕获指定变量,只有指定变量会按值传递给 Lambda。

  4. [&var]:按引用捕获指定变量,只有指定变量会按引用传递给 Lambda。

  5. [=, &var]:按值捕获所有外部变量,并按引用捕获指定变量。

示例 1:基本的 Lambda 表达式

#include <iostream>

int main() {
    // 定义一个 lambda 表达式,计算两个整数的和
    auto add = [](int a, int b) -> int {
        return a + b;
    };

    int result = add(5, 3);  // 调用 Lambda 表达式
    std::cout << "Result: " << result << std::endl;  // 输出:Result: 8
    return 0;
}

说明:

  • 捕获列表[],没有捕获外部变量。

  • 参数列表(int a, int b),表示 Lambda 表达式接收两个整数作为参数。

  • 返回类型-> int,表示 Lambda 表达式返回一个整数。

  • 函数体return a + b;,实现了加法操作。

示例 2:按值捕获外部变量

#include <iostream>

int main() {
    int x = 10;
    int y = 20;

    // 按值捕获外部变量
    auto add = [=]() -> int { 
        return x + y; 
    };

    // 修改 x 和 y 的值
    x = 15;
    y = 25;

    std::cout << "Result: " << add() << std::endl;  // 输出:Result: 30
    return 0;
}

说明:

  • 捕获列表[=],表示按值捕获所有外部变量。

  • 返回值:即使 xy 在 Lambda 表达式调用时被修改,Lambda 仍然使用它们被捕获时的值。

示例 3:按引用捕获外部变量

#include <iostream>

int main() {
    int x = 10;
    int y = 20;

    // 按引用捕获外部变量
    auto add = [&]() -> int { 
        x += 5;  // 修改外部变量 x
        return x + y; 
    };

    std::cout << "Result: " << add() << std::endl;  // 输出:Result: 35
    std::cout << "x: " << x << ", y: " << y << std::endl;  // 输出:x: 15, y: 20
    return 0;
}

说明:

  • 捕获列表[&],按引用捕获外部变量 xy,这意味着在 Lambda 表达式内对这些变量的修改会影响外部的变量。

示例 4:捕获特定变量

#include <iostream>

int main() {
    int x = 10;
    int y = 20;

    // 捕获特定变量(按值捕获 x 和按引用捕获 y)
    auto add = [x, &y]() -> int { 
        y = y + 5;  // 修改 y
        return x + y;  // 使用捕获的 x 和修改后的 y
    };

    std::cout << "Result: " << add() << std::endl;  // 输出:Result: 35
    std::cout << "x: " << x << ", y: " << y << std::endl;  // 输出:x: 10, y: 25
    return 0;
}

说明:

  • 捕获列表[x, &y],表示按值捕获 x,按引用捕获 yx 的值不会变化,而 y 会发生变化。

  • 调用结果:Lambda 使用捕获时的 x(10),同时修改 y 的值。

示例 5:无参数的 Lambda 表达式

#include <iostream>

int main() {
    // 一个不带参数的简单 lambda 表达式
    auto greet = []() { 
        std::cout << "Hello, World!" << std::endl; 
    };

    greet();  // 输出:Hello, World!
    return 0;
}

说明:

  • 捕获列表[],没有捕获外部变量。

  • 参数列表(),不接受任何参数。

  • 函数体:输出 Hello, World!

示例 6:使用 Lambda 表达式作为参数

Lambda 表达式常用于作为函数的参数,特别是在 STL 算法中。

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

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

    // 使用 lambda 表达式作为 std::for_each 的参数
    std::for_each(numbers.begin(), numbers.end(), [](int num) {
        std::cout << num << " ";
    });
    std::cout << std::endl;
    return 0;
}

说明:

  • Lambda 表达式[](int num) { std::cout << num << " "; },在 std::for_each 中使用 Lambda 遍历容器 numbers,并输出每个元素。

示例 7:Lambda 表达式的返回类型推导

C++14 引入了 Lambda 表达式返回类型推导,编译器会自动推导 Lambda 的返回类型,减少冗余的类型声明。

#include <iostream>

int main() {
    auto add = [](int a, int b) { 
        return a + b;  // 自动推导返回类型
    };

    std::cout << "Result: " << add(5, 3) << std::endl;  // 输出:Result: 8
    return 0;
}

说明:

  • 返回类型推导:没有显式声明返回类型,编译器自动推导返回值的类型(此例为 int)。

总结

Lambda 表达式在 C++ 中提供了一种简洁的方式来创建匿名函数,它有以下特点:

  • 简洁:无须为小功能编写额外的函数。

  • 灵活:能够捕获外部变量,通过值或引用捕获外部作用域中的变量。

  • 强大:广泛应用于 STL 算法中,作为函数参数传递。

使用场景:

  • 临时函数:不需要重复使用的函数可以使用 Lambda 表达式。

  • 回调函数:作为回调函数传递给 STL 算法或事件处理器。

  • 事件处理:在 GUI 编程中,Lambda 表达式常用于信号和槽的连接。

Lambda 表达式极大地提高了代码的简洁性和可读性,是现代 C++ 编程的重要工具之一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值