惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
V
V2EX
博客园 - 【当耐特】
WordPress大学
WordPress大学
爱范儿
爱范儿
美团技术团队
宝玉的分享
宝玉的分享
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
小众软件
小众软件
量子位
Hugging Face - Blog
Hugging Face - Blog
B
Blog RSS Feed
Recorded Future
Recorded Future
Engineering at Meta
Engineering at Meta
雷峰网
雷峰网
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
M
MIT News - Artificial intelligence
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 聂微东
H
Hackread – Cybersecurity News, Data Breaches, AI and More
腾讯CDC
大猫的无限游戏
大猫的无限游戏
Jina AI
Jina AI
博客园 - 叶小钗
GbyAI
GbyAI
Y
Y Combinator Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
F
Full Disclosure
G
Google Developers Blog
D
Docker
T
Tailwind CSS Blog
C
Check Point Blog
Last Week in AI
Last Week in AI
人人都是产品经理
人人都是产品经理
T
The Blog of Author Tim Ferriss
B
Blog
博客园 - 三生石上(FineUI控件)
博客园 - Franky
H
Help Net Security
MyScale Blog
MyScale Blog
U
Unit 42
D
DataBreaches.Net
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
I
InfoQ
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
L
LangChain Blog
有赞技术团队
有赞技术团队
Martin Fowler
Martin Fowler
Microsoft Security Blog
Microsoft Security Blog

Jun's Blog

2023 年度总结 OS 学习记 之 XV6 如何编译 GraalVM LLVM 中端优化之 InstCombine C++ 中 inline 关键字的语义 链接与库 C++ 移动语义基础 如何优化矩阵相乘 2022年度总结 CUDA初学笔记 汇编语言之保护模式 汇编语言之实模式 浅析 libc++ 中的 string 实现 std::expected 基本使用 C++模板基础 CSAPP第九章笔记之虚拟内存 CSAPP第八章笔记之异常控制流 2021年度总结 编译安装GCC12 如何给LLVM贡献代码 CSAPP第三章笔记Part 2 CSAPP第三章笔记Part 1 GDB基本使用笔记 读Effetive Modern C++ 之类型推导 给计算机新生的一封信 CMake学习笔记 由Redis学习数据结构--字典 由Redis学习数据结构--链表 对Python及爬虫行业的思考 Vim实用技巧 浅谈C++中的类 简要剖析const关键字 正则表达式基础总结 使用Hugo和Firebase部署个人博客 浅谈HTTPS证书 Linux常用命令总结 Git学习笔记 Linux下使用v2ray 娱乐至死读书笔记 自控力读书笔记 少有人走的路力读书笔记 Postfix & Dovecot 自建邮箱服务 Linux用户管理 Linux开机流程 Linux文件与目录 Linux硬盘管理 Linux服务浅谈 About Me
C++中lambda表达式基础
Jun · 2021-04-26 · via Jun's Blog

· Jun

语法

完整语法

1
2
3
4
[ capture clause ] (parameters) -> return-type  
{   
   definition of method   
} 

简化语法

在没有形参的情况下,可以简化为:

本质

lambda本质是一个函数对象,可以看作是重载了函数调用运算符的对象。

1
2
3
4
5
6
7
8
struct absInt {
  int operator()(int val) const {
    return val < 0 ? -val : val;
  }
}

absInt a;
int abs_a = a(10);

举例

1
2
3
4
5
int main(){
  int id {0};
  auto f = [id]() { std::cout << id << "\n"; };
  f();
}

上面这个lambda在编译器看来将是下面这个情况:

1
2
3
4
5
6
7
class __lambda_8_12 {
    public: 
        __lambda_8_12(int& _id) : id{_id} {}
        inline  void operator()() { std::cout << id << "\n"; }
    private: 
        int id;
};

捕获

  • 捕获只能针对于lambda式的作用域内可见的非静态局部变量,包括形参。

  • lambda表达式可以直接使用静态变量,而不需要被捕获。

默认按值捕获

[=]

以下面的代码为例,lambda表达式f捕获的id是值为0的那一个。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int main()
{
  int id {0};
  auto f = [=]() {
    std::cout << "id = " << id << "\n";
    std::cout << "id addr inside: "<< &id << "\n";
  };
  
  id = 100;
  std::cout << "id addr outside: " << &id << "\n";
  f();
  return 0;
}

下面的代码和上面的区别在于mutable。不加mutable的情况下,id是只读的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int main(){
  int id {0};
  auto f = [=]() mutable {
    std::cout << "id = " << id << "\n";
    std::cout << "id addr inside: "<< &id << "\n";
    ++id;//没有mutable的情况下,这段代码会报错。error: read-only variable is not assignable
  };
  
  id = 100;
  std::cout << "id addr outside: " << &id << "\n";
  f();
  f();
  return 0;
}

输出

1
2
3
4
5
id addr outside: 0x7ffe932c0b88
id = 0
id addr inside: 0x7ffe932c0b80
id = 1
id addr inside: 0x7ffe932c0b80

可以看到,lambda捕获的id是一个全新的变量,即使我们将id重新赋值为10,它也不受影响。

默认按引用捕获

[&]

lambda表达式里的id和外部的id是同一个变量,改变外部变量的情况下也会影响内部。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int main(){
  int id {0};
  auto f = [&id]() {
    std::cout << "id = " << id << "\n";
    std::cout << "id addr inside: "<< &id << "\n";
    ++id;
  };
  
  id = 100;
  std::cout << "id addr outside: " << &id << "\n";
  f();
  f();
  return 0;
}

输出

1
2
3
4
5
id addr outside: 0x7ffde0080a68
id = 100
id addr inside: 0x7ffde0080a68
id = 101
id addr inside: 0x7ffde0080a68

可以看到,lambda内部捕获的id就是外部的那个,当我们对其重新赋值的时候,lamnda内部也该改变了。

但是这种按引用捕获可能会导致闭包包含局部变量的引用,当局部变量的生命周期结束后,lamnda将失效,从而引起未定义的行为。

初始化捕获/广义lambda捕获 (C++14)

在=号的两边,是不同的作用域。在其左边,是lambda的成员变量,而在右边,则是初始化表达式。

1
2
3
[a=a]
[a=std::move(a)]
[a=std::unique_ptr<a>()]
  • 第一个表达式可以看作是在lambda中创建一个id变量,直接用外部的id初始化它。从此二者分离开,互不干扰。
  • 第二个表达式可以看作是在lambda中创建一个id变量,用外部的id的右值去初始化它。
  • 第三个表达式中,我们甚至可以直接在lambda表达式中初始化id变量。