Lambda expressions (since C++11)
构造一个 闭包 (能够捕获作用域中变量的无名函数对象)。
| 目录 | 
语法
无显式模板参数列表的Lambda表达式(可能为非泛型)
| 
          
           [
          
         捕获列表
         
         
          
           ]
          
         前属性
         
         
         
          (可选)
          
           (
          
         参数
         
         
          
           )
          
         说明符
         
         
         
          (可选)
         
         
          异常说明
         
         
         
          (可选)后属性  (可选) 尾随返回类型  (可选) 要求  (可选) 契约规范  (可选) 
          
           {
          
         函数体
          
           }
          
          | (1) | ||||||||
| 
          
           [
          
         捕获列表
         
         
          
           ] {
          
         函数体
          
           }
          
          | (2) | (C++23 前) | |||||||
| 
          
           [
          
         捕获列表
         
         
          
           ]
          
         前属性
         
         
         
          (可选)
         
         
          尾随返回类型
         
         
         
          (可选)
         
         
          契约规范
         
         
         
          (可选)
          
           {
          
         函数体
          
           }
          
          | (2) | (C++23 起) | |||||||
| 
          
           [
          
         捕获列表
         
         
          
           ]
          
         前属性
         
         
         
          (可选)
         
         
          异常说明后属性  (可选) 尾随返回类型  (可选) 契约规范  (可选) 
          
           {
          
         函数体
          
           }
          
          | (3) | (C++23 起) | |||||||
| 
          
           [
          
         捕获列表
         
         
          
           ]
          
         前属性
         
         
         
          (可选)
         
         
          说明符
         
         
          异常说明
         
         
         
          (可选)后属性  (可选) 尾随返回类型  (可选) 契约规范  (可选) 
          
           {
          
         函数体
          
           }
          
          | (4) | (C++23 起) | |||||||
带显式模板参数列表的Lambda表达式(始终为泛型) (since C++20)
| 
          
           [
          
         捕获
         
         
          
           ] <
          
         模板参数
         
         
          
           >
          
         模板要求
         
         
         
          (可选)前属性  (可选) 
          
           (
          
         参数
         
         
          
           )
          
         说明符
         
         
         
          (可选)
         
         
          异常说明
         
         
         
          (可选)后属性  (可选) 尾随返回类型  (可选) 要求子句  (可选) 契约规范  (可选) 
          
           {
          
         函数体
          
           }
          
          | (1) | ||||||||
| 
          
           [
          
         捕获
         
         
          
           ] <
          
         模板参数
         
         
          
           >
          
         模板要求
         
         
         
          (可选)
          
           {
          
         函数体
          
           }
          
          | (2) | (C++23 前) | |||||||
| 
          
           [
          
         捕获
         
         
          
           ] <
          
         模板参数
         
         
          
           >
          
         模板要求
         
         
         
          (可选)前属性  (可选) 尾随返回类型  (可选) 契约规范  (可选) 
          
           {
          
         函数体
          
           }
          
          | (2) | (C++23 起) | |||||||
| 
          
           [
          
         捕获
         
         
          
           ] <
          
         模板参数
         
         
          
           >
          
         模板要求
         
         
         
          (可选)
         
         
          前属性
         
         
         
          (可选)
         
         
          异常说明后属性  (可选) 尾随返回类型  (可选) 契约规范  (可选) 
          
           {
          
         函数体
          
           }
          
          | (3) | (C++23 起) | |||||||
| 
          
           [
          
         捕获
         
         
          
           ] <
          
         模板参数
         
         
          
           >
          
         模板要求
         
         
         
          (可选)
         
         
          前属性
         
         
         
          (可选)
         
         
          说明符
         
         
          异常说明
         
         
         
          (可选)后属性  (可选) 尾随返回类型  (可选) 契约规范  (可选) 
          
           {
          
         函数体
          
           }
          
          | (4) | (C++23 起) | |||||||
说明
| captures | - | 指定需要被 捕获 的实体。 | ||||||||||||
| tparams | - | 非空的逗号分隔
         
          模板参数
         
         列表,用于为泛型lambda的模板参数提供名称(参见下文 
          ClosureType::operator()
         )。 | ||||||||||||
| t-requires | - | 为
         
          tparams
         
         添加
         
          约束
         
         。 
 | ||||||||||||
| front-attr | - | (C++23 起)
         
         应用于闭包类型的
         
          
           operator
           
            (
           
           
            )
           
          
         
         的
         
          属性说明符序列
         
         (因此可使用 
          [[
          
           noreturn
          
          ]]
         属性)。 | ||||||||||||
| params | - | 闭包类型的
         
          
           operator
           
            (
           
           
            )
           
          
         
         的
         
          形参列表
         
         。 
 | ||||||||||||
| specs | - | 下列说明符的列表,每个说明符在每个序列中最多出现一次。 
 | ||||||||||||
| except | - | 为闭包类型的 operator ( ) 提供 动态异常规范 或 (C++20 前) noexcept 说明符 。 | ||||||||||||
| back-attr | - | 应用于闭包类型的
         
          
           operator
           
            (
           
           
            )
           
          
         
         类型的
         
          属性说明符序列
         
         (因此不能使用 
          [[
          
           noreturn
          
          ]]
         属性)。 | ||||||||||||
| trailing | - | 
          
           ->
          
         ret
         
         ,其中
         
          ret
         
         指定返回类型。 | ||||||||||||
| requires | - | (C++20 起) 为闭包类型的 operator ( ) 添加 约束 。 | ||||||||||||
| contract-specs | - | (C++26 起) 闭包类型的 operator ( ) 的 函数契约说明符 列表。 | ||||||||||||
| body | - | 函数体。 | 
       
      
| 
          若将
          
            | (自 C++14 起) | 
在 函数体 起始处会隐式定义一个变量 __func__ ,其语义说明详见 此处 。
闭包类型
lambda 表达式是一种独特的无名非 联合 非 聚合 类类型的纯右值表达式,称为 闭包类型 。该类型(出于 ADL 目的)声明于包含该 lambda 表达式的最小块作用域、类作用域或命名空间作用域内。
| 当且仅当 捕获列表 为空时,该闭包类型为 结构类型 。 | (C++20 起) | 
闭包类型拥有以下成员,它们不能被 显式实例化 、 显式特化 ,或 (since C++14) 在 友元声明 中指名:
ClosureType:: operator()( params )
| 
            
             ret operator
             
              (
             
             
              )
             
             
              (
             
             params
             
              )
             
             
              {
             
             body
             
              }
             
            
            | (可能包含 static 和 const,详见下文) | |
| 
            
             
              template
             
             
              <
             
             template
             
              -
             
             params
             
              >
             
              ret operator ( ) ( params ) { body } | (C++14 起) (泛型 lambda,可能包含 static 和 const,详见下文) | |
当被调用时执行 lambda 表达式的主体。访问变量时,访问其被捕获的副本(对于按值捕获的实体),或访问原始对象(对于按引用捕获的实体)。
operator ( ) 的参数列表是 params (如果提供了该参数列表),否则参数列表为空。
operator ( ) 的返回类型是在 trailing 中指定的类型。
如果未提供 trailing ,则 operator ( ) 的返回类型会自动 推导 。 [1]
除非在 lambda 说明符中使用了关键字 mutable ,或存在显式对象参数 (C++23 起) ,否则 operator ( ) 的 cv 限定符是 const ,并且按值捕获的对象在此 operator ( ) 内部不可修改。不允许显式的 const 限定符。 operator ( ) 永远不会是虚函数,也不能具有 volatile 限定符。
| 如果 operator ( ) 满足 constexpr 函数 的要求,则它始终是 constexpr。如果在 lambda 说明符中使用了关键字 constexpr ,它也是 constexpr。 | (C++17 起) | 
| 如果在 lambda 说明符中使用了关键字 consteval ,则 operator ( ) 是一个 立即函数 。 | (C++20 起) | 
| 如果在 lambda 说明符中使用了关键字 static ,则 operator ( ) 是一个 静态成员函数 。 如果 params 包含显式对象参数,则 operator ( ) 是一个 显式对象成员函数 。 | (C++23 起) | 
        
       
| 对于 params 中类型被指定为 auto 的每个参数,会按出现顺序向 template-params 添加一个发明的模板参数。如果对应的函数成员是函数参数包,则发明的模板参数可以是 参数包 。 | (C++14 起) | 
        
       
| 如果 lambda 定义使用了显式模板参数列表,则该模板参数列表将与 operator ( ) 一起使用。对于 params 中类型被指定为 auto 的每个参数,会向该模板参数列表的末尾追加一个额外的发明模板参数: | (C++20 起) | 
lambda 表达式上的异常规范 except 适用于 operator ( ) 。
出于 名称查找 、确定 this 指针 的类型和值以及访问非静态类成员的目的,闭包类型的 operator ( ) </span
- ↑ 尽管函数返回类型推导在 C++14 中引入,但其规则在 C++11 中已可用于 lambda 返回类型推导。
ClosureType:: operator ret (*)( params )()
| 
            无捕获的非泛型 lambda
            | ||
| 
            
             
              using
             
             F
             
              =
             
             ret
             
              (
             
             
              *
             
             
              )
             
             
              (
             
             params
             
              )
             
             
              ;
             
              operator F ( ) const noexcept ; | (C++17 前) | |
| 
            
             
              using
             
             F
             
              =
             
             ret
             
              (
             
             
              *
             
             
              )
             
             
              (
             
             params
             
              )
             
             
              ;
             
              constexpr operator F ( ) const noexcept ; | (C++17 起) | |
| 
            无捕获的泛型 lambda
            | ||
| 
            
             
              template
             
             
              <
             
             template
             
              -
             
             params
             
              >
             
             
              using
             
             fptr_t
             
              =
             
             
              /* 见下文 */
             
             
              ;
             
              
              
               template
              
              
               <
              
              template
              
               -
              
              params
              
               >
              
               | (C++14 起) (C++17 前) | |
| 
            
             
              template
             
             
              <
             
             template
             
              -
             
             params
             
              >
             
             
              using
             
             fptr_t
             
              =
             
             
              /* 见下文 */
             
             
              ;
             
              
              
               template
              
              
               <
              
              template
              
               -
              
              params
              
               >
              
               | (C++17 起) | |
此 用户定义转换函数 仅在 lambda 表达式没有 captures  且没有显式对象参数 (C++23 起) 时定义。它是闭包对象的公开、 constexpr、 (C++17 起) 非虚、非显式、const noexcept 成员函数。
| 如果函数调用运算符(或对于泛型 lambda 的特化)是立即函数,则此函数是 立即函数 。 | (C++20 起) | 
| 泛型无捕获 lambda 拥有与 operator ( ) 相同的虚构模板形参列表的用户定义转换函数模板。 | (C++14 起) | 
        
       
| 转换函数返回的值是指向具有 C++ 语言链接 的函数的指针,当调用时,其效果与在闭包类型的默认构造实例上调用闭包类型的函数调用运算符相同。 | (C++14 前) | 
| 转换函数(模板)返回的值是指向具有 C++ 语言链接 的函数的指针,当调用时,其效果与以下相同: 
 | (C++14 起) (C++23 前) | 
| 转换函数(模板)返回的值是 | (C++23 起) | 
        
       
| 如果函数调用运算符(或对于泛型 lambda 的特化)是 constexpr,则此函数是 constexpr。 | 
ClosureType:: ClosureType()
| 
            
             ClosureType
             
              (
             
             
              )
             
             
              =
             
             
              default
             
             
              ;
             
            
            | (since C++20) (仅当未指定任何捕获时) | |
| 
            
             ClosureType
             
              (
             
             
              const
             
             ClosureType
             
              &
             
             
              )
             
             
              =
             
             
              default
             
             
              ;
             
            
            | ||
| 
            
             ClosureType
             
              (
             
             ClosureType
             
              &&
             
             
              )
             
             
              =
             
             
              default
             
             
              ;
             
            
            | ||
| 闭包类型不满足 默认构造要求 。闭包类型没有默认构造函数。 | (until C++20) | 
| 如果未指定 捕获 ,则闭包类型具有默认的默认构造函数。否则,它没有默认构造函数(这包括存在 默认捕获 的情况,即使它实际上未捕获任何内容)。 | (since C++20) | 
ClosureType:: operator=(const ClosureType&)
| 
            
             ClosureType
             
              &
             
             operator
             
              =
             
             
              (
             
             
              const
             
             ClosureType
             
              &
             
             
              )
             
             
              =
             
             delete
             
              ;
             
            
            | (C++20 前) | |
| 
            
             ClosureType
             
              &
             
             operator
             
              =
             
             
              (
             
             
              const
             
             ClosureType
             
              &
             
             
              )
             
             
              =
             
             
              default
             
             
              ;
             
              ClosureType & operator = ( ClosureType && ) = default ; | (C++20 起) (仅当未指定任何捕获时) | |
| 
            
             ClosureType
             
              &
             
             operator
             
              =
             
             
              (
             
             
              const
             
             ClosureType
             
              &
             
             
              )
             
             
              =
             
             delete
             
              ;
             
            
            | (C++20 起) (其他情况) | |
| 复制赋值运算符被定义为已删除(且移动赋值运算符未声明)。闭包类型不满足 可复制赋值 要求。 | (C++20 前) | 
| 若未指定 捕获 ,则闭包类型具有默认的复制赋值运算符和默认的移动赋值运算符。否则,它具有已删除的复制赋值运算符(包括存在 捕获默认 的情况,即使其实际未捕获任何内容)。 | (C++20 起) | 
ClosureType:: ~ClosureType()
| 
            
             ~ClosureType
             
              (
             
             
              )
             
             
              =
             
             
              default
             
             
              ;
             
            
            | ||
析构函数为隐式声明。
ClosureType:: Captures
| 
            
             T1 a
             
              ;
             
              
              T2 b
              
               ;
              
               | ||
        若lambda表达式通过复制捕获任何内容(隐式使用捕获子句
        
         
          [=]
         
        
        或显式使用不包含&字符的捕获,例如
        
         
          [a, b, c]
         
        
        ),则闭包类型包含以未指定顺序声明的无名非静态数据成员,这些成员持有所有被如此捕获实体的副本。
       
对应无初始化器的捕获的数据成员,在lambda表达式求值时进行 直接初始化 。对应带初始化器的捕获的数据成员,按初始化器要求进行初始化(可能是复制初始化或直接初始化)。若捕获数组,则按索引递增顺序直接初始化数组元素。数据成员的初始化顺序与其声明顺序一致(该顺序未指定)。
每个数据成员的类型是对应被捕获实体的类型,除非该实体具有引用类型(此时,对函数的引用会被捕获为被引用函数的左值引用,而对对象的引用会被捕获为被引用对象的副本)。
        对于通过引用捕获的实体(使用
        
         capture-default
        
        
         
          [&]
         
        
        或使用&字符时,例如
        
         
          [&a, &b, &c]
         
        
        ),闭包类型中是否声明额外数据成员是未指定的
        
         
          ,但任何此类额外成员必须满足
          
           
            LiteralType
           
          
          要求
         
         
          
           (C++17起)
          
         
        
        。
       
       
      
| Lambda表达式不允许出现在 未求值表达式 、 模板参数 、 别名声明 、 typedef声明 中,也不允许出现在函数(或函数模板)声明中除函数体和函数 默认参数 之外的任何位置。 | (C++20 前) | 
Lambda 捕获
捕获列表 定义了可从 lambda 函数体内部访问的外部变量。其语法定义如下:
| 捕获默认 | (1) | ||||||||
| 捕获列表 | (2) | ||||||||
| 捕获默认 
          
           ,
          
         捕获列表 | (3) | ||||||||
| capture-default | - | 
          
           &
          
         和
          
           =
          
         其中之一 | 
| capture-list | - | 以逗号分隔的 capture 列表 | 
       
       
        捕获
       
       的语法定义如下:
      
| 标识符 | (1) | ||||||||
| 标识符 
          
           ...
          
          | (2) | ||||||||
| 标识符 初始化器 | (3) | (C++14 起) | |||||||
| 
          
           &
          
         标识符 | (4) | ||||||||
| 
          
           &
          
         标识符
          
           ...
          
          | (5) | ||||||||
| 
          
           &
          
         标识符
         
         
          初始化器 | (6) | (C++14 起) | |||||||
| 
          
           this
          
          | (7) | ||||||||
| 
          
           *
          
         
          
           this
          
          | (8) | (C++17 起) | |||||||
| 
          
           ...
          
         标识符
         
         
          初始化器 | (9) | (C++20 起) | |||||||
| 
          
           &
          
         
          
           ...
          
         标识符
         
         
          初始化器 | (10) | (C++20 起) | |||||||
       如果
       
        捕获默认符
       
       是
       
        
         &
        
       
       ,后续的简单捕获不得以
       
        
         &
        
       
       开头。
      
struct S2 { void f(int i); }; void S2::f(int i) { [&] {}; // 正确:默认引用捕获 [&, i] {}; // 正确:引用捕获,但 i 通过拷贝捕获 [&, &i] {}; // 错误:当默认捕获为引用时不能显式声明引用捕获 [&, this] {}; // 正确,等价于 [&] [&, this, i] {}; // 正确,等价于 [&, i] }
       如果
       
        捕获默认符
       
       是
       
        
         =
        
       
       ,后续的简单捕获必须以
       
        
         &
        
       
       
        
         开头或是
         
          
           *this
          
         
        
        
         
          (C++17起)
         
        
       
       
        
         或
         
          
           this
          
         
        
        
         
          (C++20起)
         
        
       
       。
      
struct S2 { void f(int i); }; void S2::f(int i) { [=] {}; // 正确:默认按值捕获 [=, &i] {}; // 正确:按值捕获,但 i 按引用捕获 [=, *this] {}; // C++17 前:错误:无效语法 // C++17 起:正确:按值捕获外围的 S2 对象 [=, this] {}; // C++20 前:错误:当 = 为默认捕获时不能显式捕获 this // C++20 起:正确,与 [=] 相同 }
任何捕获只能出现一次,且其名称必须与任何参数名称不同:
struct S2 { void f(int i); }; void S2::f(int i) { [i, i] {}; // 错误:重复捕获 i [this, *this] {}; // 错误:重复捕获 "this" (C++17) [i] (int i) {}; // 错误:参数与捕获变量同名 }
如果变量满足以下条件,lambda表达式可以不捕获而直接使用该变量:
如果变量满足以下条件,lambda表达式可以在不捕获的情况下读取其值:
- 具有常量非易失性整型或枚举类型,且已使用 常量表达式 进行初始化,或
- 是 constexpr 类型且不包含可变成员。
       当前对象(
       
        
         
          *
         
         this
        
       
       )在存在任一捕获默认符时可被隐式捕获。若被隐式捕获,它始终通过引用捕获,即使捕获默认符为
       
        
         =
        
       
       。
       
        
         当捕获默认符为
         
          
           =
          
         
         时对
         
          
           
            *
           
           this
          
         
         的隐式捕获已被弃用。
        
        
         
          (since C++20)
         
        
       
      
仅满足以下任一条件的lambda表达式可以具有不带初始化器的 capture-default 或 capture :
| (since C++26) | 
对于此类 lambda 表达式, 可达作用域 被定义为从最内层封闭函数(及其参数)开始的所有外围作用域集合。这包括嵌套的块作用域,以及当该 lambda 嵌套时外围 lambda 的作用域。
       在任意不带初始化器的捕获项(除
       
        this
       
       捕获外)中,
       
        标识符
       
       会通过 lambda 的
       
        可达作用域
       
       中使用常规
       
        非限定名称查找
       
       进行查找。查找结果必须是声明于可达作用域中且具有自动存储期的
       
        变量
       
       
        
         ,或是其对应变量满足上述要求的
         
          结构化绑定
         
        
        
         
          (C++20 起)
         
        
       
       。该实体被
       
        显式捕获
       
       。
      
| 
          带有初始化器的捕获(称为
          
           初始化捕获
          
          )的行为类似于声明并显式捕获一个以类型说明符
          
            
 这用于通过诸如 x = std :: move ( x ) 的捕获方式来捕获仅移动类型。 这也使得通过常量引用捕获成为可能,例如使用 & cr = std:: as_const ( x ) 或类似方式。 int x = 4; auto y = [&r = x, x = x + 1]() -> int { r += 2; return x * x; }(); // 将 ::x 更新为 6 并将 y 初始化为 25。 | (C++14 起) | 
如果 captures 具有 capture-default 且未显式捕获封闭对象(作为 this 或 * this ),或在 lambda 主体中 可 ODR 使用 的自动变量 ,或对应变量具有原子存储期的 结构化绑定 (C++20 起) ,则当该实体在表达式中的潜在求值表达式(包括在使用非静态类成员前隐式添加 this - > 时)内被命名时,会隐式捕获该实体。
       为确定隐式捕获的目的,
       
        
         typeid
        
       
       永远不会被视为使其操作数处于未求值状态。
      
| 即使实体仅在 lambda 体实例化后的 被丢弃语句 中被命名,也可能被隐式捕获。 | (since C++17) | 
void f(int, const int (&)[2] = {}) {} // #1 void f(const int&, const int (&)[1]) {} // #2 struct NoncopyableLiteralType { constexpr explicit NoncopyableLiteralType(int n) : n_(n) {} NoncopyableLiteralType(const NoncopyableLiteralType&) = delete; int n_; }; void test() { const int x = 17; auto l0 = []{ f(x); }; // 正确:调用 #1,不捕获 x auto g0 = [](auto a) { f(x); }; // 同上 auto l1 = [=]{ f(x); }; // 正确:捕获 x(自 P0588R1 起)并调用 #1 // 该捕获可被优化消除 auto g1 = [=](auto a) { f(x); }; // 同上 auto ltid = [=]{ typeid(x); }; // 正确:捕获 x(自 P0588R1 起) // 即使 x 是未求值操作数 // 该捕获可被优化消除 auto g2 = [=](auto a) { int selector[sizeof(a) == 1 ? 1 : 2] = {}; f(x, selector); // 正确:是依赖表达式,因此捕获 x }; auto g3 = [=](auto a) { typeid(a + x); // 无论 a + x 是否为未求值操作数 // 都会捕获 x }; constexpr NoncopyableLiteralType w{42}; auto l4 = []{ return w.n_; }; // 正确:w 未被 ODR 使用,无需捕获 // auto l5 = [=]{ return w.n_; }; // 错误:w 需要通过拷贝捕获 }
如果 lambda 表达式的主体 ODR 使用 了通过复制捕获的实体,则访问的是闭包类型的成员。如果未 ODR 使用该实体,则访问的是原始对象:
void f(const int*); void g() { const int N = 10; [=] { int arr[N]; // 非odr使用:引用的是g函数中的常量N f(&N); // odr使用:导致N被(以复制方式)捕获 // &N是闭包对象成员N的地址,而非g函数中N的地址 }(); }
如果 lambda 表达式通过引用方式捕获引用并对其进行 ODR 使用,它实际使用的是原始引用所指向的对象,而非被捕获的引用本身:
#include <iostream> auto make_function(int& x) { return [&] { std::cout << x << '\n'; }; } int main() { int i = 3; auto f = make_function(i); // f 中对 x 的使用直接绑定到 i i = 5; f(); // 正确:输出 5 }
       在具有捕获默认值
       
        
         =
        
       
       的 lambda 表达式主体内,任何可捕获实体的类型都如同被捕获一样处理(因此如果该 lambda 不是
       
        mutable
       
       ,通常会添加 const 限定),即使该实体处于未求值操作数中且未被实际捕获(例如在
       
        
         decltype
        
       
       中):
      
void f3() { float x, &r = x; [=] { // x 和 r 未被捕获(在 decltype 操作数中的出现不是 odr-use) decltype(x) y1; // y1 具有 float 类型 decltype((x)) y2 = y1; // y2 具有 float const& 类型,因为此 lambda // 不是 mutable 且 x 是左值 decltype(r) r1 = y1; // r1 具有 float& 类型(不考虑转换) decltype((r)) r2 = y2; // r2 具有 float const& 类型 }; }
任何被lambda捕获的实体(隐式或显式)都会被该lambda表达式进行odr使用(因此,嵌套lambda的隐式捕获会触发外层lambda的隐式捕获)。
所有隐式捕获的变量必须在 lambda 的 可达作用域 内声明。
如果 lambda 捕获了外围对象(通过 this 或 * this ),则其最接近的外围函数必须是非静态成员函数,或者该 lambda 必须位于 默认成员初始化器 中:
struct s2 { double ohseven = .007; auto f() // 以下两个lambda表达式的最内层封闭函数 { return [this] // 通过引用捕获封闭的s2对象 { return [*this] // 通过拷贝捕获封闭的s2对象 (C++17) { return ohseven; // 正确 } }(); } auto g() { return [] // 不捕获任何变量 { return [*this] {}; // 错误:外部lambda表达式未捕获*this }(); } };
如果 lambda 表达式 (或泛型 lambda 函数调用运算符的特化) (since C++14) ODR-使用 * this 或任何具有自动存储期的变量,则必须被该 lambda 表达式捕获。
void f1(int i) { int const N = 20; auto m1 = [=] { int const M = 30; auto m2 = [i] { int x[N][M]; // N 和 M 未被 odr 使用 // (它们未被捕获是允许的) x[0][0] = i; // i 被 m2 显式捕获 // 并被 m1 隐式捕获 }; }; struct s1 // f1() 内的局部类 { int f; void work(int n) // 非静态成员函数 { int m = n * n; int j = 40; auto m3 = [this, m] { auto m4 = [&, j] // 错误:j 未被 m3 捕获 { int x = n; // 错误:n 被 m4 隐式捕获 // 但未被 m3 捕获 x += m; // 正确:m 被 m4 隐式捕获 // 并被 m3 显式捕获 x += i; // 错误:i 超出可达作用域 // (作用域终止于 work()) x += f; // 正确:this 被 m4 隐式捕获 // 并被 m3 显式捕获 }; }; } }; }
类成员无法通过无初始化器的捕获显式捕获(如上所述,只有 变量 允许出现在 捕获列表 中):
class S { int x = 0; void f() { int i = 0; // auto l1 = [i, x] { use(i, x); }; // 错误:x 不是变量 auto l2 = [i, x = x] { use(i, x); }; // 正确,复制捕获 i = 1; x = 1; l2(); // 调用 use(0,0) auto l3 = [i, &x = x] { use(i, x); }; // 正确,引用捕获 i = 2; x = 2; l3(); // 调用 use(1,2) } };
       当 lambda 通过隐式按值捕获方式捕获成员时,它并不会复制该成员变量:对成员变量
       
        m
       
       的使用会被视为表达式
       
        
         
          (
         
         
          *
         
         this
         
          )
         
         .
         
          m
         
        
       
       ,而
       
        
         
          *
         
         this
        
       
       始终会被隐式地按引用捕获:
      
class S { int x = 0; void f() { int i = 0; auto l1 = [=] { use(i, x); }; // 捕获 i 的副本和 this 指针的副本 // i = 1; x = 1; l1(); // 调用 use(0, 1),如同 // i 按值捕获且 x 按引用捕获 auto l2 = [i, this] { use(i, x); }; // 与上述相同,显式指定 i = 2; x = 2; l2(); // 调用 use(1, 2),如同 // i 按值捕获且 x 按引用捕获 auto l3 = [&] { use(i, x); }; // 按引用捕获 i 并 // 捕获 this 指针的副本 i = 3; x = 2; l3(); // 调用 use(3, 2),如同 // i 和 x 均按引用捕获 auto l4 = [i, *this] { use(i, x); }; // 创建 *this 的副本, // 包括 x 的副本 i = 4; x = 4; l4(); // 调用 use(3, 2),如同 // i 和 x 均按值捕获 } };
如果 lambda 表达式出现在 默认参数 中,它不能显式或隐式捕获任何内容 ,除非所有捕获都具有满足默认参数中表达式约束的初始化器 (since C++14) :
void f2() { int i = 1; void g1( int = [i] { return i; }() ); // 错误:捕获了外部变量 void g2( int = [i] { return 0; }() ); // 错误:捕获了外部变量 void g3( int = [=] { return i; }() ); // 错误:捕获了外部变量 void g4( int = [=] { return 0; }() ); // 正确:无捕获 void g5( int = [] { return sizeof i; }() ); // 正确:无捕获 // C++14 void g6( int = [x = 1] { return x; }() ); // 正确:数字1可以出现在默认参数中 // void g7( int = [x = i] { return x; }() ); // 错误:变量i不能出现在默认参数中 // }
匿名联合体 的成员不能被捕获。 位域 只能通过复制方式捕获。
       如果嵌套的 lambda
       
        m2
       
       捕获了某个也被直接外层 lambda
       
        m1
       
       捕获的实体,那么
       
        m2
       
       的捕获将按以下方式转换:
      
- 
        如果外层 lambda
        m1通过复制捕获,m2捕获的是m1闭包类型的非静态成员,而非原始变量或 * this ;若m1不可变,其非静态数据成员被视为 const 限定。
- 
        如果外层 lambda
        m1通过引用捕获,m2捕获的是原始变量或 * this 。
#include <iostream> int main() { int a = 1, b = 1, c = 1; auto m1 = [a, &b, &c]() mutable { auto m2 = [a, b, &c]() mutable { std::cout << a << b << c << '\n'; a = 4; b = 4; c = 4; }; a = 3; b = 3; c = 3; m2(); }; a = 2; b = 2; c = 2; m1(); // 调用 m2() 并输出 123 std::cout << a << b << c << '\n'; // 输出 234 }
| 若 lambda 表达式捕获了任何内容,则其函数调用运算符的显式对象参数(若存在)的类型只能是: 
 struct C { template<typename T> C(T); }; void func(int i) { int x = [=](this auto&&) { return i; }(); // 正确 int y = [=](this C) { return i; }(); // 错误 int z = [](this C) { return 42; }(); // 正确 auto lambda = [n = 42] (this auto self) { return n; }; using Closure = decltype(lambda); struct D : private Closure { D(Closure l) : Closure(l) {} using Closure::operator(); friend Closure; }; D{lambda}(); // 错误 } | (C++23 起) | 
注释
| 功能测试宏 | 值 | 标准 | 功能特性 | 
|---|---|---|---|
| 
           __cpp_lambdas
           | 
           200907L
           | (C++11) | Lambda表达式 | 
| 
           __cpp_generic_lambdas
           | 
           201304L
           | (C++14) | 泛型Lambda表达式 | 
| 
           201707L
           | (C++20) | 泛型Lambda的显式模板参数列表 | |
| 
           __cpp_init_captures
           | 
           201304L
           | (C++14) | Lambda初始化捕获 | 
| 
           201803L
           | (C++20) | 允许在Lambda初始化捕获中使用包展开 | |
| 
           __cpp_capture_star_this
           | 
           201603L
           | (C++17) | 通过值捕获 * this 作为 [ = , * this ] | 
| 
           __cpp_constexpr
           | 
           201603L
           | (C++17) | constexpr lambda | 
| 
           __cpp_static_call_operator
           | 
           202207L
           | (C++23) | 无捕获Lambda的静态 operator ( ) | 
隐式 lambda 捕获规则因缺陷报告 P0588R1 发生了细微变化。截至 2023年10月,部分主流实现尚未完全应用该缺陷报告,因此在某些情况下仍在使用基于检测 ODR使用 的旧规则。
| P0588R1 之前的旧规则 | ||
|---|---|---|
| 
           若
           
            captures
           
           具有
           
            capture-default
           
           且未显式捕获封闭对象(作为
            
 
 | 
示例
本示例展示了如何将lambda传递给泛型算法,以及如何将lambda表达式生成的对象存储在 std::function 对象中。
#include <algorithm> #include <functional> #include <iostream> #include <vector> int main() { std::vector<int> c{1, 2, 3, 4, 5, 6, 7}; int x = 5; c.erase(std::remove_if(c.begin(), c.end(), [x](int n) { return n < x; }), c.end()); std::cout << "c: "; std::for_each(c.begin(), c.end(), [](int i) { std::cout << i << ' '; }); std::cout << '\n'; // 闭包的类型无法命名,但可以用auto推断 // 自C++14起,lambda可以拥有默认参数 auto func1 = [](int i = 6) { return i + 4; }; std::cout << "func1: " << func1() << '\n'; // 与所有可调用对象一样,闭包可以被捕获到std::function中 //(这可能会带来不必要的开销) std::function<int(int)> func2 = [](int i) { return i + 4; }; std::cout << "func2: " << func2(6) << '\n'; constexpr int fib_max {8}; std::cout << "模拟`递归lambda`调用:\n斐波那契数列: "; auto nth_fibonacci = [](int n) { std::function<int(int, int, int)> fib = [&](int n, int a, int b) { return n ? fib(n - 1, a + b, a) : b; }; return fib(n, 0, 1); }; for (int i{1}; i <= fib_max; ++i) std::cout << nth_fibonacci(i) << (i < fib_max ? ", " : "\n"); std::cout << "lambda递归的替代方法:\n斐波那契数列: "; auto nth_fibonacci2 = [](auto self, int n, int a = 0, int b = 1) -> int { return n ? self(self, n - 1, a + b, a) : b; }; for (int i{1}; i <= fib_max; ++i) std::cout << nth_fibonacci2(nth_fibonacci2, i) << (i < fib_max ? ", " : "\n"); #ifdef __cpp_explicit_this_parameter std::cout << "C++23的lambda递归方法:\n"; auto nth_fibonacci3 = [](this auto self, int n, int a = 0, int b = 1) -> int { return n ? self(n - 1, a + b, a) : b; }; <
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 应用于 | 发布时的行为 | 正确行为 | 
|---|---|---|---|
| CWG 974 | C++11 | lambda 表达式的参数列表中不允许使用 默认参数 | 允许 | 
| CWG 1048 ( N3638 ) | C++11 | 返回类型只能对仅包含一个
         
          
           return
          
         
         语句的 lambda 函数体进行推导 | 改进了返回 类型推导 | 
| CWG 1249 | C++11 | 未明确外层非可变 lambda 捕获的成员是否 被视为 const | 视为 const | 
| CWG 1557 | C++11 | 未指定闭包类型转换函数返回的 函数类型的语言链接 | 具有 C++ 语言链接 | 
| CWG 1607 | C++11 | lambda 表达式可以出现在 函数和函数模板签名中 | 不允许 | 
| CWG 1612 | C++11 | 可以捕获匿名联合体的成员 | 不允许 | 
| CWG 1722 | C++11 | 无捕获 lambda 的转换函数 具有未指定的异常规范 | 转换函数 为 noexcept | 
| CWG 1772 | C++11 | lambda 函数体中 __func__ 的语义不明确 | 它引用闭包 类的 operator() | 
| CWG 1780 | C++14 | 未明确泛型 lambda 闭包类型的成员 是否可以显式实例化或显式特化 | 两者都不允许 | 
| CWG 1891 | C++11 | 闭包具有被删除的默认构造函数 和隐式复制/移动构造函数 | 无默认构造函数且具有默认 复制/移动构造函数 | 
| CWG 1937 | C++11 | 关于调用转换函数结果的效果, 未指定在哪个对象上调用其 operator ( ) 具有相同效果 | 在闭包类型的 默认构造实例上 | 
| CWG 1973 | C++11 | 闭包类型的
         
          
           operator
           
            (
           
           
            )
           
          
         
         的参数列表 可能引用 trailing 中给定的参数列表 | 只能引用 params | 
| CWG 2011 | C++11 | 对于通过引用捕获的引用,未指定 捕获标识符引用哪个实体 | 它引用最初 被引用的实体 | 
| CWG 2095 | C++11 | 通过复制捕获函数右值引用的 行为不明确 | 已明确 | 
| CWG 2211 | C++11 | 如果捕获名称与参数名称相同, 行为未指定 | 此种情况下程序 非良构 | 
| CWG 2358 | C++14 | 出现在默认参数中的 lambda 表达式必须 是无捕获的,即使所有捕获都使用可以出现在 默认参数中的表达式初始化 | 允许带有捕获的 此类 lambda 表达式 | 
| CWG 2509 | C++17 | 每个说明符可以在说明符序列中 多次出现 | 每个说明符在 说明符序列中最多 只能出现一次 | 
| CWG 2561 | C++23 | 具有显式对象参数的 lambda 可能具有 到不期望的函数指针类型的转换函数 | 它没有这样的 转换函数 | 
| CWG 2881 | C++23 | 当继承非公开或存在歧义时,具有显式参数的 operator ( ) 可能为派生类实例化 | 使其非良构 | 
| P0588R1 | C++11 | 隐式 lambda 捕获规则检测 odr-use | 检测被简化 | 
参见
| 
           auto
          specifier
         
         
          (C++11) | 指定从表达式推导出的类型 | 
| 
           
            
             
              (C++11)
             
            
           
           | 任何可复制构造的可调用对象的可复制包装器 (类模板) | 
| 
           
            
             
              (C++23)
             
            
           
           | 支持给定调用签名中限定符的任何可调用对象的仅移动包装器 (类模板) | 
外部链接
| 嵌套函数 - 在另一个( 封闭 )函数内部定义的函数。 |