[expr.prim.lambda.closure] (original) (raw)
7 Expressions [expr]
7.5 Primary expressions [expr.prim]
7.5.6 Lambda expressions [expr.prim.lambda]
7.5.6.2 Closure types [expr.prim.lambda.closure]
The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below.
The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the correspondinglambda-expression.
[Note 1:
This determines the set of namespaces and classes associated with the closure type ([basic.lookup.argdep]).
The parameter types of a lambda-declarator do not affect these associated namespaces and classes.
— _end note_]
An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:
- the size and/or alignment of the closure type,
- whether the closure type is trivially copyable ([class.prop]),
- whether the closure type is trivially relocatable ([class.prop]),
- whether the closure type is replaceable ([class.prop]), or
- whether the closure type is a standard-layout class ([class.prop]).
An implementation shall not add members of rvalue reference type to the closure type.
[Note 2:
The function call operator template for a generic lambda can be an abbreviated function template ([dcl.fct]).
— _end note_]
[Example 1: auto glambda = [](auto a, auto&& b) { return a < b; };bool b = glambda(3, 3.14); auto vglambda = [](auto printer) { return [=](auto&& ... ts) { 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); q(); auto fact = [](this auto self, int n) -> int { return (n <= 1) ? 1 : n * self(n-1);}; std::cout << fact(5); — _end example_]
Given a lambda with a lambda-capture, the type of the explicit object parameter, if any, of the lambda's function call operator (possibly instantiated from a function call operator template) shall be either:
- the closure type,
- a class type publicly and unambiguously derived from the closure type, or
- a reference to a possibly cv-qualified such type.
[Example 2: 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; }(); } — _end example_]
It is neither virtual nor declared volatile.
The function call operator or any given operator template specialization is a constexpr function if either the corresponding lambda-expression'sparameter-declaration-clauseis followed by constexpr or consteval, or it is constexpr-suitable ([dcl.constexpr]).
[Example 3: auto ID = [](auto a) { return a; };static_assert(ID(3) == 3); struct NonLiteral { NonLiteral(int n) : n(n) { } int n;};static_assert(ID(NonLiteral{3}).n == 3); — _end example_]
[Example 4: 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()); auto two = monoid(2); assert(two() == 2); static_assert(add(one)(one)() == two()); static_assert(add(one)(one)() == monoid(2)()); — _end example_]
[Note 3:
[Example 5: template <typename T> concept C1 = ;template <std::size_t N> concept C2 = ;template <typename A, typename B> concept C3 = ;auto f = []<typename T1, C1 T2> requires C2<sizeof(T1) + sizeof(T2)> (T1 a1, T1 b1, T2 a2, auto a3, auto a4) requires C3<decltype(a4), T2> { }; — _end example_]
— _end note_]
If all potential references to a local entity implicitly captured by a lambda-expression Loccur within the function contract assertions ([dcl.contract.func]) of the call operator or operator template of Lor within assertion-statements ([stmt.contract.assert]) within the body of L, the program is ill-formed.
[Note 4:
Adding a contract assertion to an existing C++ program cannot cause additional captures.
— _end note_]
[Example 6: static int i = 0;void test() { auto f1 = [=] pre(i > 0) {}; int i = 1;auto f2 = [=] pre(i > 0) {}; auto f3 = [i] pre(i > 0) {}; auto f4 = [=] { contract_assert(i > 0); };auto f5 = [=] { contract_assert(i > 0); (void)i;};auto f6 = [=] pre( []{ bool x = true;return [=]{ return x; }(); }()) {};bool y = true;auto f7 = [=] pre([=]{ return y; }()); } — _end example_]
The closure type for a non-generic lambda-expression with nolambda-captureand no explicit object parameter ([dcl.fct]) whose constraints (if any) are satisfied has a conversion function to pointer to function with C++ language linkage having the same parameter and return types as the closure type's function call operator.
The conversion is to “pointer to noexcept function” if the function call operator has a non-throwing exception specification.
If the function call operator is a static member function, then the value returned by this conversion function is a pointer to the function call operator.
Otherwise, the value returned by this conversion function is a pointer to a function F that, when invoked, has the same effect as invoking the closure type's function call operator on a default-constructed instance of the closure type.
F is a constexpr function if the function call operator is a constexpr function and is an immediate function if the function call operator is an immediate function.
For a generic lambda with no lambda-captureand no explicit object parameter ([dcl.fct]), the closure type has a conversion function template to pointer to function.
The conversion function template has the same invented template parameter list, and the pointer to function has the same parameter types, as the function call operator template.
The return type of the pointer to function shall behave as if it were adecltype-specifier denoting the return type of the corresponding function call operator template specialization.
[Note 5:
If the generic lambda has no trailing-return-type or the trailing-return-type contains a placeholder type, return type deduction of the corresponding function call operator template specialization has to be done.
The corresponding specialization is that instantiation of the function call operator template with the same template arguments as those deduced for the conversion function template.
Consider the following:auto glambda = [](auto a) { return a; };int (*fp)(int) = glambda;
The behavior of the conversion function of glambda above is like that of the following conversion function:struct Closure { template<class T> auto operator()(T t) const { } template<class T> static auto lambda_call_operator_invoker(T a) { } 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 7: void f1(int (*)(int)) { } void f2(char (*)(int)) { } void g(int (*)(int)) { } void g(char (*)(char)) { } void h(int (*)(int)) { } void h(char (*)(int)) { } auto glambda = [](auto a) { return a; }; f1(glambda); f2(glambda); g(glambda); h(glambda); int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; — _end example_]
If the function call operator template is a static member function template, then the value returned by any given specialization of this conversion function template is a pointer to the corresponding function call operator template specialization.
Otherwise, the value returned by any given specialization of this conversion function template is a pointer to a function F that, when invoked, has the same effect as invoking the generic lambda's corresponding function call operator template specialization on a default-constructed instance of the closure type.
F is a constexpr function if the corresponding specialization is a constexpr function andF is an immediate function if the function call operator template specialization is an immediate function.
[Note 6:
This will result in the implicit instantiation of the generic lambda's body.
The instantiated generic lambda's return type and parameter types need to match the return type and parameter types of the pointer to function.
— _end note_]
[Example 8: auto GL = [](auto a) { std::cout << a; return a; };int (*GL_int)(int) = GL; GL_int(3); — _end example_]
The conversion function or conversion function template is public, constexpr, non-virtual, non-explicit, const, and has a non-throwing exception specification.
[Example 9: auto Fwd = [](int (*fp)(int), auto a) { return fp(a); };auto C = [](auto a) { return a; };static_assert(Fwd(C,3) == 3); auto NC = [](auto a) { static int s; return a; };static_assert(Fwd(NC,3) == 3); — _end example_]
[Example 10: struct S1 { int x, y;int operator()(int);void f() { [=]()->int { return operator()(this->x + y); };} }; — _end example_]
It has a defaulted copy constructor and a defaulted move constructor ([class.copy.ctor]).
[Note 7:
These special member functions are implicitly defined as usual, which can result in them being defined as deleted.
— _end note_]