C++ lambda表达式

目录

一、Lambda表达式概述

1.介绍

2.作用

1)lambda表达式做回调

2.做并行计算

二、Lambda表达式语义分析

1.基本语法分析

2.捕获列表

3.匿名函数简写

三、使用注意


一、Lambda表达式概述

1.介绍

Lambda表达式是C++11标准引入的一种特性,它提供了一种方便的方式来定义匿名函数。Lambda表达式是一种能够捕捉外部变量并使用它们的函数对象。由捕获列表、参数列表、返回类型和函数体组成;其中,参数列表和返回类型可以忽略,但不可以忽略捕获列表和函数体。

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

例如:auto f = []{ return 1 + 2; }; 或者 auto f = [](int x, int y)->int{ return x + y; };

(在后面章节会详细介绍使用规则.)

2.作用

在c++11中提出lambda特性,通过引入lambda表达式,C++11使得编写匿名函数变得更加简洁和方便,提高了代码的可读性和灵活性。其主要作用包括:简化代码、作为算法的参数、回调、并行计算等。以下举几个简单示例:

1)lambda表达式做回调

cpp 复制代码
#include <iostream>
#include <functional>
#include <vector>
#include <unistd.h>

using namespace std;

class EventHandle
{
public:
        void registerCallback(function<void()> callback)
        {
            callbacks.push_back(callback);
        }
        void triggerEvent()
        {
            for(auto &testCallback: callbacks)
            {
                testCallback();
            }
        }
private:
    vector<function<void()>>  callbacks; 
};

int main()
{
    EventHandle eventHandle;
    eventHandle.registerCallback([]{cout<<"run"<<endl;});

    sleep(3);
    eventHandle.triggerEvent(); //触发

    return 0;
}

main函数中,我们使用lambda表达式注册了一个回调函数,当事件被触发时,lambda表达式中的代码将执行,输出"run!"。这样,我们可以方便地在事件处理中使用lambda表达式,而不需要显式地定义独立的函数或类来处理回调

2.做并行计算

cpp 复制代码
#include <iostream>
#include <vector>
#include <numeric>

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

    int sum = std::accumulate(numbers.begin(), numbers.end(), 0,
        [](int a, int b) {
            return a + b;
        }
    );
    std::cout << "result:" << sum << std::endl;

    return 0;
}

lambda表达式中的操作是将两个数字相加,它接收两个参数(当前的累加结果和下一个元素),并返回它们的和。通过使用并行算法和lambda表达式,我们可以充分发挥多核处理器的能力,提高计算效率。

二、Lambda表达式语义分析

1.基本语法分析

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

cpp 复制代码
auto Add = [](int a, int b) -> int {
        return a + b;
    };

上述代码表示[ ]传参列表为空,参数为int a和int b,返回值类型为int。函数体为return a+b 的一个匿名函数。一般情况下,编译器可以自动推断出lambda表达式的返回类型,所以我们可以不指定返回类型

cpp 复制代码
auto Add = [](int a, int b){
        return a + b;
    };

注意:如果匿名函数表达式内有多个return操作,编译器无法自动推断出返回类型,此时必须指定返回类型

2.捕获列表

如果需要在匿名函数内部使用外部变量而不想传参,可以用捕获列表来传参。

cpp 复制代码
int c = 12;
int d = 30;
auto Add = [c, d](int a, int b)->int { 
   cout << "d = " << d  << endl;
   return c;};

如果在匿名函数内部,加入对变量c,d修改的操作; 则无法编译通过。因为上述捕获列表是通过值进行传递,无法进行修改。那么如何进行修改呢?通过传入引用,这样就可以当作左值被用来修改

cpp 复制代码
int c = 12;
int d = 30;
auto Add = [&c, &d](int a, int b)->int { 
   d = 11;
   cout << "d = " << d  << endl;
   return c;};

以下是捕获列表分类:

|------------------------|------------------------------------------------------------------------------------------------------------|
| [] | 空捕获列表,Lambda不能使用所在函数中的变量。 |
| [names] | names是一个逗号分隔的名字列表,这些名字都是Lambda所在函数的局部 变量。默认情况下,这些变量会被拷贝,然后按值传递,名字前面如果使用 了&,则按引用传递 |
| [&] | 隐式捕获列表,Lambda体内使用的局部变量都按引用方式传递 |
| [=] | 隐式捕获列表,Lanbda体内使用的局部变量都按值传递 |
| [&,identifier_list] | identifier_list是一个逗号分隔的列表,包含0个或多个来自所在函数的变量, 这些变量采用值捕获的方式,其他变量则被隐式捕获,采用引用方式传递, identifier_list中的名字前面不能使用&。 |
| [=,identifier_list] | identifier_list中的变量采用引用方式捕获,而被隐式捕获的变量都采用按值 传递的方式捕获。identifier_list中的名字不能包含this,且这些名字面前必须 使用& |

3.匿名函数简写

匿名函数由捕获列表、参数列表、返回类型和函数体组成;可以忽略参数列表和返回类型,但不可以忽略捕获列表和函数体,如:auto num = []{ return 1 + 2; };

三、使用注意

  • 匿名函数构建的时候对于按值传递的捕获列表,会立即将当前可以取到的值拷贝一份作为常数(相当于一个右值),然后将该常数作为参数传递
  • 可变性(mutability):默认情况下,lambda表达式不能修改捕获的按值变量。如果需要修改,可以使用mutable关键字修饰lambda表达式。
  • 如果匿名函数表达式内有多个return操作,编译器无法自动推断出返回类型,此时必须指定返回类型
  • 生命周期问题:当lambda表达式捕获了外部变量时,需要注意外部变量的生命周期,以避免悬垂引用(dangling references)或访问已销毁的对象。

关于第四点,请看示例:

cpp 复制代码
int main() {
    int* danglingPtr = nullptr;

    {
        int *p = new int(2);
        auto lambda = [&]() {
            danglingPtr = p; // 捕获局部变量 p 的地址
        };
        lambda();
        delete p;
    } // 离开作用域,p 被销毁

    // 此时 danglingPtr 包含了一个悬垂的指针,访问它是未定义行为
    std::cout << *danglingPtr << std::endl; // 可能导致崩溃或不可预测的行为

    return 0;
}
相关推荐
未来可期LJ26 分钟前
【C++ 设计模式】单例模式的两种懒汉式和饿汉式
c++·单例模式·设计模式
Trouvaille ~1 小时前
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
c++·c++20·编译原理·编译器·类和对象·rvo·nrvo
little redcap1 小时前
第十九次CCF计算机软件能力认证-乔乔和牛牛逛超市
数据结构·c++·算法
Dola_Pan1 小时前
Linux文件IO(二)-文件操作使用详解
java·linux·服务器
AI原吾2 小时前
掌握Python-uinput:打造你的输入设备控制大师
开发语言·python·apython-uinput
机器视觉知识推荐、就业指导2 小时前
Qt/C++事件过滤器与控件响应重写的使用、场景的不同
开发语言·数据库·c++·qt
毕设木哥2 小时前
25届计算机专业毕设选题推荐-基于python的二手电子设备交易平台【源码+文档+讲解】
开发语言·python·计算机·django·毕业设计·课程设计·毕设
珞瑜·2 小时前
Matlab R2024B软件安装教程
开发语言·matlab
weixin_455446172 小时前
Python学习的主要知识框架
开发语言·python·学习
孤寂大仙v2 小时前
【C++】STL----list常见用法
开发语言·c++·list