Lambda expressions
自C++11%29以来LAMBDA表达式%28
构造一个封闭%28http://en.wikipara.com/wiki/闭包_%28计算机_Science%29%29一个未命名的函数对象,能够捕获作用域中的变量。
句法
capture-list mutable(optional) constexpr(optional)(c++17) exception attribute -> ret { body } | (1) | |
---|---|---|
capture-list -> ret { body } | (2) | |
capture-list { body } | (3) | |
capture-list { body } | (4) | |
1%29完整申报。
2%29 Constlambda声明:复制捕获的对象不能修改。
3%29尾尾返回类型:闭包%27s的返回类型operator()
根据下列规则确定:
if the body consists of nothing but a single return statement with an expression, the return type is the type of the returned expression (after lvalue-to-rvalue, array-to-pointer, or function-to-pointer implicit conversion otherwise, the return type is void. | (until C++14) |
---|---|
The return type is deduced from return statements as if for a function whose return type is declared auto. | (since C++14) |
- 如果身体只有一个
return
语句,则返回类型是返回表达式%28之后的类型。从lvalue到rvalue、数组到指针或函数到指针的隐式转换。29%;
- 否则,返回类型为
void
...
%28直到C++14%29,返回类型是推演从return
语句,就像用于其函数的语句一样。返回类型声明为自动...
%28自C++14%29
4%29 Omit参数列表:函数不使用参数,就好像参数列表是()
此表单只能在以下情况下使用:不使用任何参数、可变、异常规范、属性或尾随返回类型。
解释
mutable | - | allows body to modify the parameters captured by copy, and to call their non-const member functions |
---|---|---|
constexpr(C++17) | - | explicitly specifies that the function call operator is a constexpr function. When this specifier is not present, the function call operator will be constexpr anyway, if it happens to satisfy all constexpr function requirements |
exception | - | provides the exception specification or the noexcept clause for operator() of the closure type |
attribute | - | provides the attribute specification for operator() of the closure type |
capture-list | - | a comma-separated list of zero or more captures, optionally beginning with a capture-default. Capture list can be passed as follows (see below for the detailed description): a,&b where a is captured by copy and b is captured by reference. this captures the current object (*this) by reference & captures all automatic variables used in the body of the lambda by reference and current object by reference if exists = captures all automatic variables used in the body of the lambda by copy and current object by reference if exists [] captures nothing |
params | - | The list of parameters, as in named functions, except that default arguments are not allowed (until C++14). If auto is used as a type of a parameter, the lambda is a generic lambda. (since C++14) |
ret | - | Return type. If not present it's implied by the function return statements (or void if it doesn't return any value) |
body | - | Function body |
[a,&b]
何地阿
是通过复制和乙
是通过引用捕获的。
[this]
捕获当前对象%28*
this
%29
[&]
捕捉所有自动通过引用在lambda的主体中使用的变量,如果存在的话,则使用当前对象的引用。
[=]
捕捉所有自动在lambda主体中使用的变量,如果存在,则按副本使用,如果存在,则使用当前对象引用。
[]
什么都抓不到
params - The list of parameters, as in [named functions](function), except that [default arguments](default_arguments) are not allowed (until C++14). If `auto` is used as a type of a parameter, the lambda is a _generic lambda_. (since C++14) ret - Return type. If not present it's implied by the function return statements (or void if it doesn't return any value) body - Function body
lambda表达式是一个prvalue表达式,其值为%28,直到C++17%29,其结果对象为%28,因为C++17%29是唯一未命名的非聚合类类型的未命名临时对象,称为闭合类型
,则声明为%28用于ADL%29在包含lambda表达式的最小块作用域、类作用域或命名空间范围中。闭包类型有以下成员:
闭合类型::操作员%28%29%28 params%29
ret operator()(params) const { body } | | (the keyword mutable was not used) |
---|---|---|
ret operator()(params) { body } | | (the keyword mutable was used) |
template<template-params> ret operator()(params) { body } | | (since C++14) (generic lambda) |
当调用lambda-表达式时,执行其主体。当访问变量时,访问其捕获的副本%28用于复制%29捕获的实体,或访问引用%29捕获的实体的原始对象%28。除非关键字mutable
在lambda-表达式中使用,函数调用操作符是const-限定的,并且由副本捕获的对象是不可修改的。operator()
.功能呼叫操作员从来都不是易失性的-合格的,也不是虚拟的.
The function-call operator is always constexpr if it satisfies the requirements of a constexpr function. It is also constexpr if the keyword constexpr was used in the lambda declaration. | (since C++17) |
---|---|
For every parameter in params whose type is specified as auto, an invented template parameter is added to template-params, in order of appearance. The invented template parameter may be a parameter pack if the corresponding function member of params is a function parameter pack. // generic lambda, operator() is a template with two parameters auto glambda = { return a < b; }; bool b = glambda(3, 3.14 // ok // generic lambda, operator() is a template with one parameter auto vglambda = { return = // generic lambda, ts is a parameter pack { printer(std::forward<decltype(ts)>(ts)... return = { printer(ts... }; // nullary lambda (takes no parameters) }; }; auto p = vglambda( { std::cout << v1 << v2 << v3; } auto q = p(1, 'a', 3.14 // outputs 1a3.14 q( // outputs 1a3.14 ClosureType's operator() cannot be explicitly instantiated or explicitly specialized. | (since C++14) |
lambda表达式上的异常规范异常适用于函数调用操作符或运算符模板。
为了...的目的名称查找的类型和值。这个指针对于访问非静态类成员,在lambda表达式的上下文中考虑闭包类型%27s函数调用操作符的主体。
二次
struct X {
int x, y;
int operator()(int
void f()
{
// the context of the following lambda is the member function X::f
[=]()->int
{
return operator()(this->x + y // X::operator()(this->x + (*this).y)
// this has type X*
};
}
};
二次
ClosureType
%27operator()
不能在朋友申报。
悬空引用
如果引用(隐式或显式)捕获非引用实体,并且在实体%27s生存期结束后调用闭包对象的函数调用操作符,则会发生未定义行为。C++闭包不会延长捕获引用的生存期。
同样适用于捕获对象所指向的对象的生存期。this
指针。
ClosureType::操作符RET%28%2A%29%28 Params%29%28%29
| | (capture-less non-generic lambda) |
---|---|---|
using F = ret(*)(params operator F() const; | | (until C++17) |
using F = ret(*)(params constexpr operator F() const; | | (since C++17) |
| | (capture-less generic lambda) |
template<template-params> using fptr_t = /*see below*/; template<template-params> operator fptr_t<template-params>() const; | | (since C++14) (until C++17) |
template<template-params> using fptr_t = /*see below*/; template<template-params> operator fptr_t<template-params>() const; | | (since C++17) |
这用户定义的转换函数只有在lambda表达式的捕获列表为空时才定义。它是一个公共的,因为C++17%29非虚的,非显式的,Const no除%28自C++14%29成员函数的闭包对象。
A generic captureless lambda has user-defined conversion function template with the same invented template parameter list as the function-call operator template. If the return type is empty or auto, it is obtained by return type deduction on the function template specialization, which, in turn, is obtained by template argument deduction for conversion function templates. void f1(int (*)(int)) {} void f2(char (*)(int)) {} void h(int (*)(int)) {} // #1 void h(char (*)(int)) {} // #2 auto glambda = { return a; }; f1(glambda // ok f2(glambda // error: not convertible h(glambda // ok: calls #1 since #2 is not convertible int& (*fpi)(int*) = ->auto& { return *a; }; // ok | (since C++14) |
---|
此转换函数返回的值是指向C++函数的指针。语言链接这与直接调用闭包对象%27s函数调用操作符具有相同的效果。
This function is constexpr if the function call operator (or specialization, for generic lambdas) is constexpr. auto Fwd= {return fp(a}; auto C={return a;}; static_assert(Fwd(C,3)==3//OK auto NC={static int s; return a;}; static_assert(Fwd(NC,3)==3 // error: no specialization can be constexpr because of s If the closure object's operator() has a non-throwing exception specification, then the pointer returned by this function has the type pointer to noexcept function. | (since C++17) |
---|
ClosureType::ClosureType%28%29
ClosureType() = delete; | | (until C++14) |
---|---|---|
ClosureType(const ClosureType& ) = default; | | (since C++14) |
ClosureType(ClosureType&& ) = default; | | (since C++14) |
闭包类型不是DefaultConstructible
自C++14%29默认构造函数以来,闭包类型有删除%28,直到C++14%29NO%28。复制构造函数和移动构造函数被隐式声明为%28,直到C++14%29声明为默认%28,因为C++14%29,并且可以根据复制构造函数和移动构造器...
ClosureType::Operator=%28 const ClosureType&%29
ClosureType& operator=(const ClosureType&) = delete; | | |
---|
闭包类型不是CopyAssignable
...
闭合类型::~ClosureType%28%29
~ClosureType() = default; | | |
---|
析构函数是隐式声明的。
ClosureType::Capture
T1 a; T2 b; ... | | |
---|
如果lambda-表达式通过复制%28捕获任何内容,则使用Capture子句隐式捕获。[=]
或者显式地使用不包含字符的捕获。[a, b, c]
%29,闭包类型包括未命名的非静态数据成员,它们以未指定的顺序声明,这些成员持有所有被捕获的实体的副本。
与不带初始化项的捕获对应的数据成员是直接初始化计算lambda表达式时。那些与初始化器对应的捕获被初始化,因为初始化程序要求%28可以是复制或直接初始化%29。如果捕获数组,则数组元素将按递增索引顺序直接初始化.。初始化数据成员的顺序是它们被声明为%28的顺序,它是未指定的%29。
每个数据成员的类型都是相应的捕获实体的类型,除非该实体具有引用类型%28,在这种情况下,对函数的引用被捕获为对所引用的函数的值引用,而对对象的引用被捕获为被引用对象的副本%29。
对于引用%28和默认捕获的实体[&]
或者在使用字符时&,例如。[&a, &b, &c]
%29,如果在闭包类型中声明了其他数据成员,则未指定,但任何此类额外成员都必须满足LiteralType
%28自C++17%29。
Lambda-表达式不允许进入未计算表达式,,,模板参数,,,别名声明,,,类型胡枝子f声明,在函数%28或函数模板%29声明中的任何地方,函数主体和函数%27s除外。默认参数...
Lambda俘获
捕获列表是以逗号分隔的零或多个列表。捕捉
,可以选择从捕获-默认开始。唯一的捕获默认值是。
&
%28隐式捕获ODR-使用自动变量和*this
参照%29和
=
%28隐式捕获ODR-使用的自动变量通过复制和隐式捕获*this
引用this
是ODR-使用%29。
捕获列表中单个捕获的语法是。
identifier | (1) | |
---|---|---|
identifier ... | (2) | |
identifier initializer | (3) | (C++14) |
& identifier | (4) | |
& identifier ... | (5) | |
& identifier initializer | (6) | (C++14) |
this | (7) | |
* this | (8) | (C++17) |
1%29简单的按拷贝捕获
2%29按拷贝捕获,这是一个包装膨胀
3%29逐拷贝捕获初始化器
4%29简单参考捕获
5%29的参考捕获,即包装膨胀
6%29带初始化器的参考捕获
7%29当前对象的参考捕获
8%29复制捕获当前对象
如果捕获-默认值为&
,以后的捕获不能以&
如果捕获-默认值为=
,后续捕获必须以&
或者是*this
%28自C++17%29。任何捕获只能出现一次。
二次
struct S2 { void f(int i };
void S2::f(int i)
{
[&]{}; //ok: by-reference capture default
[=]{}; //ok: by-copy capture default
[&, i]{}; // ok: by-reference capture, except i is captured by copy
[=, &i]{}; // ok: by-copy capture, except i is captured by reference
[&, &i] {}; // error: by-reference capture when by-reference is the default
[=, this] {}; // error: this when = is the default
[=, *this]{}; // ok: captures the enclosing S2 by copy (C++17)
[i, i] {}; // error: i repeated
[this, *this] {}; // error: "this" repeated (C++17)
}
二次
只有在块作用域中定义的lambda表达式才可能具有捕获-默认值或没有初始化器的捕获。对于这样的lambda表达式,到达范围
定义为直到并包括最内部的封闭函数%28及其参数%29的包围范围集。这包括嵌套块作用域和如果此lambda是嵌套的,则包含包围lambdas的作用域。
在任何捕获中没有初始化项%28的标识符,但this
-捕获%29通常使用非限定名查找在到达范围
兰博达。查找的结果必须是变量在“到达范围
”中声明自动存储持续时间。变量%28或this
%29是显式捕获
...
A capture with an initializer acts as if it declares and explicitly captures a variable declared with type auto, whose declarative region is the body of the lambda expression (that is, it is not in scope within its initializer), except that: if the capture is by-copy, the non-static data member of the closure object is another way to refer to that auto variable. if the capture is by-reference, the reference variable's lifetime ends when the lifetime of the closure object ends This is used to capture move-only types with a capture such as x = std::move(x). int x = 4; auto y = &r = x, x = x + 1->int { r += 2; return x * x; }( // updates ::x to 6 and initializes y to 25. | (since C++14) |
---|
- 如果捕获是由复制进行的,则闭包对象的非静态数据成员是引用该自动变量的另一种方式。
- 如果捕获是按引用进行的,则引用变量%27s的生存期将在闭包对象的生存期结束时结束。
这用于捕获具有捕获的仅移动类型,如x = std::move(x)
...
INT x=4;AUTOY=&r=x,x=x+1->int{r+=2;返回x%2Ax;}%28%29;//更新:x到6,并将y初始化为25。
%28自C++14%29
如果捕获列表具有捕获默认值,并且没有显式捕获包围对象%28 Asthis
或*this
%29或自动变量,它捕获它。隐式
如果。
- 羔羊的身体ODR-用途变量或
this
指针或者,变量或此指针在表达式中的一个潜在计算表达式中命名,该表达式依赖于泛型lambda参数voidf%28 int,const int%28&%29。二={}%29{}//#1 void f%28 const int&,const int%28和%29一%29{}//#2空测试%28%29{constint x=17;autoG1={f%28x%29;};//ok:调用#1,不捕获x autoG2=={int选择器大小%28a%29=1?01:2={};f%28x,选择器%29;//ok:是一个依赖表达式,因此捕获x};}%28自C++14%29
- 或变量或
this
指针是在依赖于泛型lambda参数的表达式中命名的。
无效f%28 int,const int%28&%29二={}%29{}//#1 void f%28 const int&,const int%28和%29一%29{}//#2空测试%28%29{constint x=17;autoG1={f%28x%29;};//ok:调用#1,不捕获x autoG2=={int选择器大小%28a%29=1?01:2={};f%28x,选择器%29;//ok:是一个依赖表达式,因此捕获x};}
%28自C++14%29
如果一个羔羊的身体ODR-用途复制捕获的实体,将访问闭包类型的成员。如果不是ODR--使用实体,则访问原始对象:
二次
void f(const int*
void g()
{
const int N = 10;
[=]{
int arr[N]; // not an odr-use: refers to g's const int N
f(&N // odr-use: causes N to be captured (by copy)
// &N is the address of the closure object's member N, not g's 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 // the use of x in f binds directly to i
i = 5;
f( // OK; prints 5
}
二次
在羔羊体内,任何使用解密型在任何具有自动存储时间的变量上,它就好像被捕获和使用了ODR一样,即使解密类型本身是%27T一个ODR使用,并且没有发生实际捕获:
二次
void f3() {
float x, &r = x;
[=]
{ // x and r are not captured (appearance in a decltype operand is not an odr-use)
decltype(x) y1; // y1 has type float
decltype((x)) y2 = y1; // y2 has type float const& because this lambda
// is not mutable and x is an lvalue
decltype(r) r1 = y1; // r1 has type float& (transformation not considered)
decltype((r)) r2 = y2; // r2 has type float const&
};
}
二次
由lambda%28隐式或显式地捕获的任何实体都是ODR--由lambda表达式%28使用--因此,嵌套lambda的隐式捕获将触发封装lambda%29中的隐捕获。
所有隐式捕获的变量必须在到达范围
兰博达。
如果lambda捕获了包围对象%28 Asthis
或*this
%29,最近的封闭函数必须是非静态成员函数:
二次
struct s2 {
double ohseven = .007;
auto f() { // nearest enclosing function for the following two lambdas
return [this] { // capture the enclosing s2 by reference
return [*this] { // capture the enclosing s2 by copy (C++17)
return ohseven; // OK
}
}(
}
auto g() {
return []{ // capture nothing
return [*this]{}; // error: *this not captured by outer lambda-expression
}(
}
};
二次
如果lambda表达式%28或泛型lambda%27s函数的实例化,则调用运算符%29 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 and M are not odr-used
// (ok that they are not captured)
x[0][0] = i; // i is explicitly captured by m2
// and implicitly captured by m1
};
};
struct s1 // local class within f1()
{
int f;
void work(int n) // non-static member function
{
int m = n * n;
int j = 40;
auto m3 = [this, m] {
auto m4 = [&, j] { // error: j is not captured by m3
int x = n; // error: n is implicitly captured by m4
// but not captured by m3
x += m; // ok: m is implicitly captured by m4
// and explicitly captured by m3
x += i; // error: i is outside of the reaching scope
// (which ends at work())
x += f; // ok: this is captured implicitly by m4
// and explicitly captured by m3
};
};
}
};
}
二次
类成员不能由没有上面提到的初始化器%28的捕获显式捕获,仅限于变量在捕获列表%29中允许:
二次
class S {
int x = 0;
void f() {
int i = 0;
// auto l1 = [i, x]{ use(i, x }; // error: x is not a variable
auto l2 = [i, x=x]{ use(i, x }; // OK, copy capture
i = 1; x = 1; l2( // calls use(0,0)
auto l3 = [i, &x=x]{ use(i, x }; // OK, reference capture
i = 2; x = 2; l3( // calls use(1,2)
}
};
二次
当lam
bda使用隐式复制捕获捕获成员时,它不会生成该成员变量的副本:成员变量的使用。m
被视为表达式。(*this).m
,以及this
指针是%27通过值%29捕获%28的指针。该成员的行为似乎是通过引用捕捉到的:
二次
class S {
int x = 0;
void f() {
int i = 0;
auto l1 = [=]{ use(i, x }; // captures a copy of i and a copy of the this pointer
i = 1; x = 1; l1( // calls use(0,1), as if i by copy and x by reference
auto l2 = [i, this]{ use(i, x }; // same as above, made explicit
i = 2; x = 2; l2( // calls use(1,2), as if i by copy and x by reference
auto l3 = [&]{ use(i, x }; // captures i by reference and a copy of the this pointer
i = 3; x = 2; l3( // calls use(3,2), as if i and x are both by reference
auto l4 = [i, *this]{ use(i, x }; // makes a copy of *this, including a copy of x
i = 4; x = 4; l4( // calls use(3,2), as if i and x are both by copy
}
};
二次
如果lambda表达式出现在默认参数,它不能显式或隐式地捕获任何内容。
成员匿名工会不能被俘虏。
如果嵌套的lambdam2
捕获的东西也被立即包围的lambda捕获。m1
,然后m2
%27s捕获转换如下:
- 如果封闭的羔羊
m1
复制捕获,m2
的非静态成员。m1
%27s闭包类型,而不是原始变量或this
...
- 如果封闭的羔羊
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( // calls m2() and prints 123
std::cout << a << b << c << '\n'; // prints 234
}
二次
例
此示例显示了%28a%29如何将lambda传递给一般算法;%28b%29如何将由lambda声明产生的对象存储在std::function
物品。
二次
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
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';
// the type of a closure cannot be named, but can be inferred with auto
auto func1 = [](int i) { return i + 4; };
std::cout << "func1: " << func1(6) << '\n';
// like all callable objects, closures can be captured in std::function
// (this may incur unnecessary overhead)
std::function<int(int)> func2 = [](int i) { return i + 4; };
std::cout << "func2: " << func2(6) << '\n';
}
二次
产出:
二次
c: 5 6 7
func1: 10
func2: 10
二次
缺陷报告
以下行为更改缺陷报告追溯应用于先前发布的C++标准。
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 1891 | C++14 | closure had a deleted default ctor and implicit copy/move ctors | no default and defaulted copy/move |
CWG 1722 | C++14 | the conversion function for captureless lambdas had unspecified exception specification | conversion function is noexcept |
另见
auto specifier | specifies a type defined by an expression (C++11) |
---|---|
function (C++11) | wraps callable object of any type with specified function call signature (class template) |
© cppreference.com
在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。