lambda-expression: lambda-introducer lambda-declaratoropt compound-statement
lambda-introducer: [ lambda-captureopt ]
lambda-declarator: ( parameter-declaration-clause ) decl-specifier-seqopt noexcept-specifieropt attribute-specifier-seqopt trailing-return-typeopt
Лямбда-выражения обеспечивают краткий способ создания простых функциональных объектов. [ Example:
#include <algorithm> #include <cmath> void abssort(float* x, unsigned N) { std::sort(x, x + N, [](float a, float b) { return std::abs(a) < std::abs(b); }); }
— end example ]
A lambda-expression- это prvalue, объект результата которого называетсяclosure object. A lambda-expressionне должен появляться в unevaluated operand, в template-argument, в alias-declaration, в объявлении typedef или в объявлении функции или шаблона функции вне ее тела функции и аргументов по умолчанию. [ Note: Цель состоит в том, чтобы предотвратить появление лямбда-выражений в подписи. ] [ Замыкающий объект ведет себя как объект . ] — end note Note: function object — end note
В decl-specifier-seqчасти lambda-declaratorкаждый decl-specifier должен бытьmutable либоconstexpr.
Если a lambda-expressionне включает a lambda-declarator, это как если бы lambda-declaratorбыло (). Тип возврата лямбда - этоauto, который заменяется типом, указанным в trailing-return-typeif, предоставленном и / или выведенном из return операторов, как описано в[dcl.spec.auto]. [ Example:
auto x1 = [](int i){ return i; }; // OK: return type is int auto x2 = []{ return { 1, 2 }; }; // error: deducing return type from braced-init-list int j; auto x3 = []()->auto&& { return j; }; // OK: return type is int&
— end example ]
Тип a lambda-expression(который также является типом закрывающего объекта) - это уникальный безымянный тип класса без объединения, называемый theclosure type, свойства которого описаны ниже.
Тип закрытия объявляется в области наименьшего блока, области класса или области имен, которая содержит соответствующий lambda-expression. [ Note: Это определяет набор пространств имен и классов, связанных с типом замыкания ([basic.lookup.argdep]). Типы параметров lambda-declaratorне влияют на эти связанные пространства имен и классы. ] Тип замыкания не является агрегатным типом ( ). Реализация может определять тип закрытия иначе, чем описано ниже, при условии, что это не изменяет наблюдаемое поведение программы, кроме как путем изменения: — end note [dcl.init.aggr]
размер и / или выравнивание типа укупорочного средства,
является ли тип закрытияtrivially copyable,
является ли тип закрытия astandard-layout class, или
является ли тип закрытия aPOD class.
Реализация не должна добавлять элементы ссылочного типа rvalue к типу закрытия.
Тип закрытия для неуниверсального типа lambda-expressionимеет общедоступную встроенную строкуfunction call operator , параметры и возвращаемый тип которой описываются lambda-expressionсимволами parameter-declaration-clauseи trailing-return-type соответственно. Для общей лямбды тип замыкания имеет общедоступный встроенный оператор вызова функцииmember template , который template-parameter-listсостоит из одного придуманного типа template-parameterдля каждого вхожденияauto в лямбда parameter-declaration-clauseв порядке появления. Изобретенный тип template-parameter- это пакет параметров, если соответствующий parameter-declarationобъявляет функцию параметра pack ([dcl.fct]). Тип возвращаемого значения и параметры функции шаблона оператора вызова функции являются производными от lambda-expression's trailing-return-typeи parameter-declaration-clauseпутем замены каждого вхождения auto в decl-specifiers the parameter-declaration-clauseна имя соответствующего изобретенного template-parameter. [ Example:
auto glambda = [](auto a, auto&& b) { return a < b; }; bool b = glambda(3, 3.14); // OK auto vglambda = [](auto printer) { return [=](auto&& ... ts) { // OK: ts is a function parameter pack printer(std::forward<decltype(ts)>(ts)...); return [=]() { printer(ts ...); }; }; }; auto p = vglambda( [](auto v1, auto v2, auto v3) { std::cout << v1 << v2 << v3; } ); auto q = p(1, 'a', 3.14); // OK: outputs 1a3.14 q(); // OK: outputs 1a3.14
— end example ]
Оператор вызова функции или шаблон оператора объявляется const ([class.mfct.non-static]) тогда и только тогда, когда за lambda-expression's parameter-declaration-clauseне следуетmutable. Он не виртуальный и не декларируемыйvolatile. Любое noexcept-specifierуказанное в a lambda-expression применяется к соответствующему оператору вызова функции или шаблону оператора. В attribute-specifier-seqa lambda-declaratorпринадлежит типу соответствующего оператора вызова функции или шаблона оператора. Оператор вызова функции или любая заданная специализация шаблона оператора является функцией constexpr, если либо за соответствующими lambda-expression's parameter-declaration-clauseследуютconstexpr, либо если она удовлетворяет требованиям дляconstexpr функции. [ Note: Имена, упомянутые в lambda-declarator, ищутся в контексте, в котором lambda-expressionпоявляется. ] [ — end note Example:
auto ID = [](auto a) { return a; }; static_assert(ID(3) == 3); // OK struct NonLiteral { NonLiteral(int n) : n(n) { } int n; }; static_assert(ID(NonLiteral{3}).n == 3); // ill-formed
— end example ]
[ Example:
auto monoid = [](auto v) { return [=] { return v; }; }; auto add = [](auto m1) constexpr { auto ret = m1(); return [=](auto m2) mutable { auto m1val = m1(); auto plus = [=](auto m2val) mutable constexpr { return m1val += m2val; }; ret = plus(m2()); return monoid(ret); }; }; constexpr auto zero = monoid(0); constexpr auto one = monoid(1); static_assert(add(one)(zero)() == one()); // OK // Since two below is not declared constexpr, an evaluation of its constexpr member function call operator // cannot perform an lvalue-to-rvalue conversion on one of its subobjects (that represents its capture) // in a constant expression. auto two = monoid(2); assert(two() == 2); // OK, not a constant expression. static_assert(add(one)(one)() == two()); // ill-formed: two() is not a constant expression static_assert(add(one)(one)() == monoid(2)()); // OK
— end example ]
Тип закрытия для неуниверсального типа lambda-expressionс no lambda-capture имеет функцию преобразования в указатель на функцию с языком C ++,linkage имеющую тот же параметр и возвращаемые типы, что и оператор вызова функции типа закрытия. Преобразование происходит в «указатель наnoexcept функцию», если оператор вызова функции имеет спецификацию исключения, не вызывающего выброса. Значение, возвращаемое этой функцией преобразования, является адресом функции,F которая при вызове имеет тот же эффект, что и вызов оператора вызова функции закрывающего типа. F является функцией constexpr, если оператор вызова функции является функцией constexpr. Для общей лямбды без no lambda-captureтип закрытия имеет шаблон функции преобразования в указатель на функцию. Шаблон функции преобразования изобретен так же template-parameter-list, а указатель на функцию имеет те же типы параметров, что и шаблон оператора вызова функции. Тип возвращаемого значения указателя на функцию должен вести себя так, как если бы он был decltype-specifierобозначением типа возвращаемого значения соответствующей специализации шаблона оператора вызова функции.
[ Note: Если универсальная лямбда не имеет типа заполнителя trailing-return-typeили trailing-return-typeсодержит тип-заполнитель, необходимо вывести тип возвращаемого значения соответствующей специализации шаблона оператора вызова функции. Соответствующей специализацией является создание экземпляра шаблона оператора вызова функции с теми же аргументами шаблона, что и выведенные для шаблона функции преобразования. Учтите следующее:
auto glambda = [](auto a) { return a; }; int (*fp)(int) = glambda;
Поведениеglambda вышеупомянутой функции преобразования похоже на поведение следующей функции преобразования:
struct Closure { template<class T> auto operator()(T t) const { ... } template<class T> static auto lambda_call_operator_invoker(T a) { // forwards execution to operator()(a) and therefore has // the same return type deduced ... } template<class T> using fptr_t = decltype(lambda_call_operator_invoker(declval<T>())) (*)(T); template<class T> operator fptr_t<T>() const { return &lambda_call_operator_invoker; } };
— end note ]
[ Example:
void f1(int (*)(int)) { } void f2(char (*)(int)) { } void g(int (*)(int)) { } // #1 void g(char (*)(char)) { } // #2 void h(int (*)(int)) { } // #3 void h(char (*)(int)) { } // #4 auto glambda = [](auto a) { return a; }; f1(glambda); // OK f2(glambda); // error: ID is not convertible g(glambda); // error: ambiguous h(glambda); // OK: calls #3 since it is convertible from ID int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; // OK
— end example ]
Значение, возвращаемое любой данной специализацией этого шаблона функции преобразования, является адресом функции,F которая при вызове имеет тот же эффект, что и вызов соответствующей специализации шаблона оператора вызова функции универсальной лямбды. F является функцией constexpr, если соответствующая специализация является функцией constexpr. [ Note: Это приведет к неявной реализации тела универсальной лямбды. Тип возвращаемого значения и типы параметров созданного универсального лямбда-выражения должны соответствовать типу возвращаемого значения и типам параметров указателя на функцию. ] [ — end note Example:
auto GL = [](auto a) { std::cout << a; return a; }; int (*GL_int)(int) = GL; // OK: through conversion function template GL_int(3); // OK: same as GL(3)
— end example ]
Функция преобразования или шаблон функции преобразования являются общедоступными, constexpr, не виртуальными, неявными, константными и имеют не-бросаниеexception specification. [ Example:
auto Fwd = [](int (*fp)(int), auto a) { return fp(a); }; auto C = [](auto a) { return a; }; static_assert(Fwd(C,3) == 3); // OK // No specialization of the function call operator template can be constexpr (due to the local static). auto NC = [](auto a) { static int s; return a; }; static_assert(Fwd(NC,3) == 3); // ill-formed
— end example ]
В lambda-expression«S compound-statementдает function-body([dcl.fct.def]) оператор вызова функции, но для целейname lookup, определяющего типа и значение ,this и преобразующей со id-expressions ссылкой на нестатические член класса в выражения доступа членов класса с использованием (*this) ([class.mfct.non-static]), то compound-statementрассматривается в контексте lambda-expression. [ Example:
struct S1 { int x, y; int operator()(int); void f() { [=]()->int { return operator()(this->x + y); // equivalent to S1::operator()(this->x + (*this).y) // this has type S1* }; } };
— end example ] Кроме того, переменная__func__ неявно определяется в начале compound-statementиз lambda-expression, с семантикой , как описано в[dcl.fct.def.general].
Тип закрытия, связанный с a, lambda-expressionне имеет конструктора по умолчанию и оператора присваивания удаленной копии. Он имеет конструктор копирования по умолчанию и конструктор перемещения по умолчанию ([class.copy]). [ Note: Эти специальные функции-члены неявно определены как обычно и поэтому могут быть определены как удаленные. ] — end note
Тип закрытия, связанный с a, lambda-expressionимеет неявно объявленный деструктор ([class.dtor]).
Член закрывающего типа не должен быть явно создан ([temp.explicit]), явно специализирован ([temp.expl.spec]) или назван вfriend объявлении ([class.friend]).
lambda-capture: capture-default capture-list capture-default , capture-list
capture-default: & =
capture-list: capture ...opt capture-list , capture ...opt
capture: simple-capture init-capture
simple-capture: identifier & identifier this * this
init-capture: identifier initializer & identifier initializer
Тело a lambda-expressionможет ссылаться на переменные с автоматической продолжительностью хранения и*this объект (если есть) охватывающих областей блока путем захвата этих сущностей, как описано ниже.
Если a lambda-captureвключает в себя, capture-defaultто&ни одному идентификатору в a simple-captureиз этого lambda-captureне должно предшествовать&. Если a lambda-captureвключает в себя, capture-defaultто=каждый simple-captureиз них lambda-captureдолжен иметь форму « » или « ». [ Форма является избыточным , но для совместимости с ISO C ++ 2014 ] Игнорирование появления в из , идентификатора или не должен появляться более одного раза в . [& identifier* this Note: [&,this] — end note initializers init-capturesthis lambda-capture Example:
struct S2 { void f(int i); }; void S2::f(int i) { [&, i]{ }; // OK [&, &i]{ }; // error: i preceded by & when & is the default [=, *this]{ }; // OK [=, this]{ }; // error: this when = is the default [i, i]{ }; // error: i repeated [this, *this]{ }; // error: this appears twice }
— end example ]
A, lambda-expressionчья наименьшая охватывающая область видимости - этоblock scope alocal lambda expression; любой другой lambda-expressionне должен иметь capture-defaultили simple-captureв своем lambda-introducer.reaching scope Локального лямбда - выражения является множество вмещающих прицелы вплоть до самой внутренней функции и ее параметры ограждающих. [ Note: Этот охват включает любое вмешательство lambda-expressions. ] — end note
Поиск identifierв a simple-captureвыполняется по обычным правилам дляunqualified name lookup; каждый такой поиск должен найти объект. Сущность, обозначенная a simple-capture , называетсяexplicitly capturedи должна быть*this (когда simple-capture это «this» или «* this») или переменной с автоматической продолжительностью хранения, объявленной в области охвата локального лямбда-выражения.
Если a identifierв a simple-captureпоявляется как declarator-idпараметр lambda-declarators parameter-declaration-clause, программа имеет неправильный формат. [ Example:
void f() {
int x = 0;
auto g = [x](int x) { return 0; } // error: parameter and simple-capture have the same name
}
— end example ]
An init-captureведет себя так, как будто он объявляет и явно захватывает переменную формы « », декларативной областью которой является 's , за исключением того, что: auto init-capture;lambda-expressioncompound-statement
если захват осуществляется копированием (см. ниже), нестатический член данных, объявленный для захвата, и переменная обрабатываются как два разных способа ссылки на один и тот же объект, у которого есть время жизни нестатического члена данных, и дополнительное копирование и уничтожение не производится, и
если захват осуществляется по ссылке, время жизни переменной заканчивается, когда заканчивается время жизни закрывающего объекта.
[ Note: Это позволяет использовать init-captureподобное «x = std::move(x)»; второй «x» должен быть привязан к объявлению в окружающем контексте. ] [ — end note Example:
int x = 4; auto y = [&r = x, x = x+1]()->int { r += 2; return x+2; }(); // Updates ::x to 6, and initializes y to 7. auto z = [a = 42](int a) { return 1; } // error: parameter and local variable have the same name
— end example ]
A lambda-expressionсо связанным capture-default, который явно не захватывает,*this или переменная с автоматической продолжительностью хранения (это исключает все, id-expression что было обнаружено как относящееся к init-captureсвязанному нестатическому элементу данных), называетсяimplicitly capture сущностью (т. Е. *this Или переменной) если compound-statement:
odr-uses сущность (в случае переменной),
odr-usesthis (в случае объекта, обозначенного*this), или
именует сущность вpotentially evaluated выражении, в котором включающее полное выражение зависит от общего лямбда-параметра, объявленного в пределах достигаемой области lambda-expression.
[ Example:
void f(int, const int (&)[2] = {}) { } // #1 void f(const int&, const int (&)[1]) { } // #2 void test() { const int x = 17; auto g = [](auto a) { f(x); // OK: calls #1, does not capture x }; auto g2 = [=](auto a) { int selector[sizeof(a) == 1 ? 1 : 2]{}; f(x, selector); // OK: is a dependent expression, so captures x }; }
— end example ] Все такие неявно захваченные объекты должны быть объявлены в пределах области действия лямбда-выражения. [ Note: Неявный захват объекта вложенным lambda-expressionможет вызвать его неявный захват содержащим lambda-expression(см. Ниже). Неявное использование odrthis может привести к неявному захвату. ] — end note
Сущность -captured это если она захвачена явно или неявно. Сущность, захваченная объектом, lambda-expressionнаходитсяodr-used в области, содержащей lambda-expression. Если*this он захвачен локальным лямбда-выражением, его ближайшая включающая функция должна быть нестатической функцией-членом. Если a lambda-expressionили создание экземпляра шаблона оператора вызова функции универсальной лямбда-выраженияodr-usesthis или переменной с автоматической продолжительностью хранения из достигаемой области действия, этот объект должен быть захвачен lambda-expression. Если a lambda-expressionзахватывает сущность, и эта сущность не определена или не зафиксирована в непосредственно включающем лямбда-выражении или функции, программа сформирована неправильно. [ Example:
void f1(int i) { int const N = 20; auto m1 = [=]{ int const M = 30; auto m2 = [i]{ int x[N][M]; // OK: N and M are not odr-used x[0][0] = i; // OK: i is explicitly captured by m2 and implicitly captured by m1 }; }; struct s1 { int f; void work(int n) { int m = n*n; int j = 40; auto m3 = [this,m] { auto m4 = [&,j] { // error: j not captured by m3 int x = n; // error: n implicitly captured by m4 but not captured by m3 x += m; // OK: m implicitly captured by m4 and explicitly captured by m3 x += i; // error: i is outside of the reaching scope x += f; // OK: this captured implicitly by m4 and explicitly by m3 }; }; } }; } struct s2 { double ohseven = .007; auto f() { return [this] { return [*this] { return ohseven; // OK } }(); } auto g() { return [] { return [*this] { }; // error: *this not captured by outer lambda-expression }(); } };
— end example ]
lambda-expressionПоявляется в аргументе по умолчанию не будет неявно или явно захватить любой объект. [ Example:
void f2() { int i = 1; void g1(int = ([i]{ return i; })()); // ill-formed void g2(int = ([i]{ return 0; })()); // ill-formed void g3(int = ([=]{ return i; })()); // ill-formed void g4(int = ([=]{ return 0; })()); // OK void g5(int = ([]{ return sizeof i; })()); // OK }
— end example ]
Сущность - этоcaptured by copy если
он неявно захватывается, capture-defaultесть=, а захваченный объект нет*this, или
он явно захваченный с захватом , который не в форме this, или . & identifier& identifier initializer
Для каждой сущности, захваченной копией, в типе замыкания объявляется безымянный нестатический член данных. Порядок объявления этих членов не указан. Тип такого члена данных - это ссылочный тип, если сущность является ссылкой на объект, ссылка lvalue на ссылочный тип функции, если сущность является ссылкой на функцию, или тип соответствующей захваченной сущности в противном случае. Член анонимного союза не может быть записан копией.
Каждый элемент id-expressionвнутри compound-statementa, lambda-expressionкоторый являетсяodr-use сущностью, захваченной копией, преобразуется в доступ к соответствующему безымянному элементу данных типа закрытия. [ , Что не является ODR использование относится к оригинальной сущности, никогда не члену типа закрытия. Более того, такое не вызывает неявного захвата объекта. ] Если захватывается копией, каждое использование odr преобразуется в указатель на соответствующий безымянный элемент данных типа замыкания, на тип . [ Приведение гарантирует, что преобразованное выражение является prvalue. ] Элемент внутри элемента a, который является случайным использованием ссылки, захваченной посредством ссылки, относится к объекту, к которому привязана захваченная ссылка, а не к захваченной ссылке. [ Действительность таких захватов определяется временем жизни объекта, на который ссылается ссылка, а не временем жизни самой ссылки. ] [ Note: id-expressionid-expression — end note *this this cast this Note: — end note id-expressioncompound-statementlambda-expression Note: — end note Example:
void f(const int*); void g() { const int N = 10; [=] { int arr[N]; // OK: not an odr-use, refers to automatic variable f(&N); // OK: causes N to be captured; &N points to // the corresponding member of the closure type }; } auto h(int &r) { return [&] { ++r; // Valid after h returns if the lifetime of the // object to which r is bound has not ended }; }
— end example ]
Сущность -captured by reference это если она неявно или явно захвачена, но не копируется. Не указано, объявляются ли дополнительные безымянные нестатические элементы данных в типе замыкания для сущностей, захваченных по ссылке. Если объявлено, такие нестатические элементы данных должны иметь буквальный тип. [ Example:
// The inner closure type must be a literal type regardless of how reference captures are represented.
static_assert([](int n) { return [&n] { return ++n; }(); }(3) == 4);
— end example ] Битовое поле или член анонимного объединения не должны фиксироваться ссылкой.
Если a lambda-expressionm2 захватывает сущность, и эта сущность захватывается немедленным включением lambda-expression m1, то m2захват преобразуется следующим образом:
еслиm1 захватывает сущность копией, m2 захватывает соответствующий нестатический член данных типаm1закрытия;
еслиm1 захватывает объект по ссылке, m2 захватывает тот же объект, захваченныйm1.
[ Example: Вложенные лямбда-выражения и вызовы, приведенные ниже, будут выведены 123234.
int a = 1, b = 1, c = 1; auto m1 = [a, &b, &c]() mutable { auto m2 = [a, b, &c]() mutable { std::cout << a << b << c; a = 4; b = 4; c = 4; }; a = 3; b = 3; c = 3; m2(); }; a = 2; b = 2; c = 2; m1(); std::cout << a << b << c;
— end example ]
Каждое вхождение в скобки ,decltype((x)) гдеx возможно заключенное в скобки id-expressionимя объекта с автоматической продолжительностью хранения, обрабатывается так, как если бы оноx было преобразовано в доступ к соответствующему члену данных типа закрытия, который был бы объявлен, если быx было использование обозначенной сущности odr. [ Example:
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& }; }
— end example ]
Когда lambda-expressionоценивается, объекты, захваченные копией, используются для прямой инициализации каждого соответствующего нестатического элемента данных результирующего объекта закрытия, а нестатические элементы данных, соответствующие объекту, init-captures инициализируются, как указано соответствующими initializer(которые может быть копией или прямой инициализацией). (Для членов массива элементы массива инициализируются напрямую в порядке возрастания индекса.) Эти инициализации выполняются в (неуказанном) порядке, в котором объявлены нестатические элементы данных. [ Note: Это гарантирует, что разрушения будут происходить в порядке, обратном построению. ] — end note
[ Note: Если не ссылочный объект неявно или неявно захватывается ссылкой, вызов оператора вызова функции соответствующего lambda-expression после того, как время жизни объекта закончилось, вероятно, приведет к неопределенному поведению. ] — end note
А, simple-captureза которым следует многоточие, - этоpack expansion. Знак, init-captureза которым следует многоточие, имеет неправильный формат. [ Example:
template<class... Args> void f(Args... args) { auto lm = [&, args...] { return g(args...); }; lm(); }
— end example ]