• C++总结(9):Lambda表达式详解


    C++ Lambda表达式允许我们定义匿名函数对象,它既可以内联使用,也可以作为参数传递。它以更方便简洁的方式创建匿名函数,因为我们不需要在单独的类或结构中重载()运算符。

    1 基本Lambda语法

    一个基本的Lambda表达式可以是这样的:

    auto greet = []() {
      // lambda function body
    };
    
    • 1
    • 2
    • 3

    其中:

    • []:表示lambda表达式的开始
    • ():参数列表,类似于普通函数的()运算符

    这里auto关键字来自动推断lambda表达式的返回类型。上面的代码等价于:

    void greet() {
      // function body
    }
    
    • 1
    • 2
    • 3

    所以就像普通函数一样,我们可以直接调用greet()

    例:基本Lambda函数

    #include 
    using namespace std;
    
    int main() {
    
      // create a lambda function that prints "Hello World!"
      auto greet = []() {
        cout << "Hello World!";
      };
    
      // call lambda function
      greet();
    
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    创建了lambda函数并将其分配给一个名为greet的变量,然后使用greet变量和()运算符调用lambda函数。

    例:带参数的Lambda函数

    #include 
    using namespace std;
    
    int main() {
      auto add = [] (int a, int b) {
       cout << "Sum = " << a + b;
      };
    
      // call the lambda function
      add(100, 78);
    
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    上面的Lambda函数等于:

    void add(int a, int b) {
      cout << "Sum = " << a + b; 
    }
    
    • 1
    • 2
    • 3

    例:带返回值的Lambda函数
    编译器可以根据返回语句隐式推断出Lambda表达式的返回类型。

    auto add = [] (int a, int b) {
      // always returns an 'int'
      return a + b;
    };
    
    • 1
    • 2
    • 3
    • 4

    在上面的例子中,我们没有明确地定义Lambda函数的返回类型。这是因为只有一个return语句,它总是返回一个整数值。

    但是如果有多个return语句,必须显式地定义类型

    auto operation = []  (int a, int b,  string op) -> double {
      if (op == "sum") {
        // returns integer value
        return a + b;
      } 
      else {
        // returns double value
        return (a + b) / 2.0;
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    上面的代码 ->double明确地将返回类型定义为 double。因此,无论各种return语句返回什么类型的值,它们都会被显式转换为double类型。

    2 Lambda函数捕获语法

    默认情况下,Lambda函数不能访问封闭函数(包含Lambda表达式的函数)的变量。为了访问这些变量,我们使用捕获子句。

    2.1 值捕获

    这类似于按值调用函数,在这种情况下,Lambda表达式创建时会拷贝实际值,所以原变量的值是无法修改的。

    int num_main = 100;
    // get access to num_main from the enclosing function
    auto my_lambda = [num_main] () {
      cout << num_main;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里, num_main允许Lambda访问num_main变量。完整例子:

    #include
    using namespace std;
    
    int main() {
    
      int initial_sum = 100;
    
      // capture initial_sum by value
      auto add_to_sum = [initial_sum] (int num) {
        // here inital_sum = 100 from local scope
        return initial_sum + num;
      };
    
      int final_sum = add_to_sum(78);
      cout << "100 + 78 = " << final_sum;
    
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    假设我们想按值捕获多个变量:

    auto my_lambda = [a, b, c, d, e] (){
      // lambda body
    }
    
    • 1
    • 2
    • 3

    这样就显得很冗长,我们可以隐式按值捕获所有变量

    auto my_lambda = [=] (){
      // lambda body
    }
    
    • 1
    • 2
    • 3

    这里的[=]表示封闭函数内的所有值都被捕获。

    2.2 引用捕获

    这类似于按引用调用函数,在这种情况下,Lambda表达式可以访问变量的地址。

    int num_main = 100;
    
    // access the address of num_main variable
    auto my_lambda = [&num_main] () {
      num_main = 900;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    完整例子:

    #include 
    using namespace std;
    
    int main() {
    
      int num = 0;
    
      cout << "Initially, num = " << num << endl;
      
      // [&num] captures num by reference
      auto increment_by_one = [&num] () {
        cout << "Incrementing num by 1.\n";
        num++;
      };
    
      // invoke lambda function
      increment_by_one();
    
      cout << "Now, num = " << num << endl;
    
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    和按值捕获类似,如果要按引用捕获封闭函数中的所有变量,可以用[&]

    auto my_lambda = [&] (){
      // lambda body
    }
    
    • 1
    • 2
    • 3

    3 STL中将Lambda函数用作参数

    来看一个求vector元素中的偶数个数的例子:

    #include 
    #include 
    #include 
    
    using namespace std;
    
    int main() {
    
      // initialize vector of integers
      vector nums = {1, 2, 3, 4, 5, 8, 10, 12};
    
      int even_count = count_if(nums.begin(), nums.end(), [](int num) {
        return num % 2 == 0;
      });
    
      cout << "There are " << even_count << " even numbers.";
    
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这里的count_if中的一个函数,用于对指定范围内的元素执行某种条件测试,并返回满足该条件的元素数量。函数原型如下:

    template< class InputIt, class UnaryPredicate >  
    int count_if( InputIt first, InputIt last, UnaryPredicate p );
    
    • 1
    • 2

    其中:

    • firstlast:起始和结束迭代器
    • p:一元谓词,用于测试每个元素是否满足某个条件

    函数会遍历从firstlast的元素,并对每个元素调用谓词p。如果谓词对于某个元素返回true,则count_if函数会增加计数器。最后,函数返回满足谓词的元素数量。


    现在再回来看:

    int even_count = count_if(nums.begin(), nums.end(), [](int num) {
      return num % 2 == 0;
    });
    
    • 1
    • 2
    • 3

    这里Lambda表达式作为count_if的第三个参数,接受整数num,如果num是偶数,则返回true。

    • 这里不需要写返回值,auto也不用写,编译器会自动推断。

    上面的例子就等价于:

    #include   
    #include   
    #include   
      
    using namespace std;  
    
    bool isEven(int num) {  
      return num % 2 == 0;  
    }  
      
    int main() {  
      vector nums = {1, 2, 3, 4, 5, 8, 10, 12};  
      int even_count = count_if(nums.begin(), nums.end(), isEven);  
      cout << "There are " << even_count << " even numbers.";  
      return 0;  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    极简实用PyTorch记录——如何读取图片、改变大小、转化为numpy数组、转化为tensor
    标签类目体系(面向业务的数据资产设计方法论)-读书笔记5
    Ajax了解及请求方式
    SpringBoot技术在商场应急管理中的创新应用
    吃透这份“腾讯限量版”Java架构笔记,要个40k不过分吧
    【wpf】ListView 和 ItemsControl 的一点区别
    useEffect中防抖为什么不起作用?react hooks中如何写防抖?
    Arcmap操作系列:80平面转经纬度84
    apache isis,基于springboot的生产力提升百倍的快速开发平台
    “云浮云福保”暖心回归! 保障升级价格不变,医保个账可为全家缴费!
  • 原文地址:https://blog.csdn.net/tilblackout/article/details/134276111