Тип 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]).