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

推荐订阅源

U
Unit 42
T
The Blog of Author Tim Ferriss
H
Help Net Security
博客园 - 叶小钗
云风的 BLOG
云风的 BLOG
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
DataBreaches.Net
博客园 - 聂微东
A
About on SuperTechFans
大猫的无限游戏
大猫的无限游戏
P
Proofpoint News Feed
Martin Fowler
Martin Fowler
博客园 - 【当耐特】
S
SegmentFault 最新的问题
Blog — PlanetScale
Blog — PlanetScale
酷 壳 – CoolShell
酷 壳 – CoolShell
G
Google Developers Blog
I
InfoQ
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
GbyAI
GbyAI
B
Blog
Engineering at Meta
Engineering at Meta
V
V2EX
Hugging Face - Blog
Hugging Face - 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++中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变量。