C++ Lambda表达式第一篇, 闭合Closuretype
- ClosureType::operator()(params)
- auto 模板参数类型
- 显式模板参数类型
- 其他
- ClosureType::operator ret(*)(params)()
lambda 表达式是唯一的未命名,非联合,非聚合类类型(称为闭包类型)的纯右值表达式,它在包含 lambda 的最小块作用域、类作用域或命名空间作用域中声明(出于 ADL 的目的)表达。
当且仅当 captures 为空时,闭包类型才是结构类型。
闭包类型具有以下成员,它们不能显式实例化、显式专门化或在友元声明中命名:
ClosureType::operator()(params)
ret operator()(params) { body }
template<template-params>
ret operator()(params) { body }
调用时,执行 lambda 表达式的主体。访问变量时,访问其捕获的副本(对于通过副本捕获的实体)或原始对象(对于通过引用捕获的实体)。
如果提供了operator()的参数列表,则为params,否则参数列表为空。
operator()的返回类型是trailing-type中指定的类型。
如果未提供 Trailing-type,则自动推导出operator() 的返回类型。
除非在 lambda 说明符中使用关键字 mutable,或者存在显式对象参数,否则 operator() 的 cv 限定符为 const,并且通过 copy 捕获的对象从内部不可修改这个运算符()。不允许显式 const 限定符。 operator() 从来都不是虚拟的,并且不能具有 volatile 限定符。
- 如果operator() 满足constexpr 函数的要求,则它始终是constexpr。如果 lambda 说明符中使用了关键字 constexpr,那么,它也是 constexpr。
- 如果 lambda 说明符中使用了关键字 consteval,则operator() 是立即函数。
- 如果 lambda 说明符中使用了关键字 static,则operator() 是静态成员函数。
- 如果 params 包含显式对象参数,则operator() 是显式对象成员函数。
auto 模板参数类型
对于 params 中类型指定为 auto 的每个参数,都会按照出现的顺序将发明的模板参数添加到 template-params 中。如果params对应的函数成员是函数参数包,则本发明的模板参数可以是参数包。
#include <iostream>
#include <iostream>
#include <fstream>
using namespace std;
// generic lambda, operator() is a template with two parameters
auto glambda = [](auto a, auto&& b) { return a < b; };
// generic lambda, operator() is a template with one parameter
auto vglambda = [](auto printer)
{
return [=](auto&&... ts) // generic lambda, ts is a parameter pack
{
printer(forward<decltype(ts)>(ts)...);
// nullary lambda (takes no parameters):
return [=] { printer(ts...); };
};
};
auto p = vglambda([](auto v1, auto v2, auto v3)
{
cout << v1 << " " << v2 << " " << v3 << endl;
});
int main() {
int x = 100;
bool b = glambda(3, (x / 10) - 3.14);
cout << b << endl;
b = glambda(3, (x / 20) - 3.14);
cout << b << endl;
auto q = p(1, 'a', 3.14); // outputs 1 a 3.14
q(); // outputs 1 a 3.14
auto pp = vglambda(printf);
pp("%s %d \n", "Sam", 45);
}
代码运行的屏幕输出
1
0
1 a 3.14
1 a 3.14
Sam 45
显式模板参数类型
如果 lambda 定义使用显式模板参数列表,则该模板参数列表将与 operator() 一起使用。对于 params 中类型指定为 auto 的每个参数,一个新的模板参数类型,将作为该模板参数列表的类型,直至到参数列表的末尾:
#include <iostream>
#include <iostream>
#include <fstream>
using namespace std;
struct A
{
A(int&& n) { cout << "rvalue overload, n=" << n << '\n'; }
A(int& n) { cout << "lvalue overload, n=" << n << '\n'; }
};
class foo
{
public:
template<class T1, class T2, class T3>
foo(T1&& t1, T2&& t2, T3&& t3) :
a1_{forward<T1>(t1)},
a2_{forward<T2>(t2)},
a3_{forward<T3>(t3)}
{}
private:
A a1_, a2_, a3_;
};
// generic lambda, operator() is a template with two parameters
auto glambda = []<class T>(T a, auto&& b) { return a < b; };
// generic lambda, operator() is a template with one parameter pack
auto f1 = []<typename... Ts>(Ts&&... ts)
{
return foo(forward<Ts>(ts)...);
};
int main() {
int x = 100;
bool b = glambda(3, (x / 10) - 3.14);
cout << b << endl;
b = glambda(5.0, (x / 20) - 3.14);
cout << b << endl;
f1(1, 2, 4);
}
1
0
rvalue overload, n=1
rvalue overload, n=2
rvalue overload, n=4
其他
lambda 表达式上的异常规范exception适用于operator()。
为了名称查找、确定 this 指针的类型和值,以及访问非静态类成员,闭包类型的operator() 的主体可以认为是 lambda 表达式的一部分。
struct X
{
int x, y;
int operator()(int);
void f()
{
// 下面的lambda表达式是成员函数 X::f
[=]() -> int
{
return operator()(this->x + y); // X::operator()(this->x + (*this).y)
// this has type X*
};
}
};
ClosureType::operator ret(*)(params)()
- 无捕获,非常规Lambda
* using F = ret(*)(params);
operator F() const noexcept;
* using F = ret(*)(params);
* constexpr operator F() const noexcept;
- 无捕获,常规lambda
* template<template-params>
operator fptr_t<template-params>() const noexcept;
* template<template-params>
constexpr operator fptr_t<template-params>() const noexcept;
仅当 lambda 表达式的捕获列表为空时,才会定义此用户定义的转换函数。该函数是一个闭合对象的成员函数, 而且具有public, constexpr, 非虚、非显式 和 const noexcept特征。
如果函数调用运算符是立即函数,则此函数是立即函数。
通用的无捕获 lambda 具有一个用户定义的转换函数模板,它具有与 operator() 相同的新模板参数表。
#include <iostream>
using namespace std;
void f1(int (*f)(int)) {
int x = f(2);
cout << "f=" << x << endl;
}
void f2(char (*)(int)) {}
void h(int (*h)(int)) { // #1
int x = h(3);
cout << "h=" << x << endl;
}
void h(char (*)(int)) {} // #2
auto glambda = [](auto a) { return a; };
int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; // OK
int main()
{
f1(glambda); // OK
// auto y = f2(glambda); // error: not convertible
h(glambda); // OK: calls #1 since #2 is not convertible
auto x = glambda(1);
cout << "x: " << x << endl;
auto y = fpi(&x);
cout << "y: " << y << endl;
}
转换函数的返回值是一个指向具有 C++ 语言链接的函数指针,调用该函数时,与在闭包类型的默认构造实例上调用闭包类型的函数调用运算符具有相同的效果。
转换函数(模板)返回的值是一个指向具有 C++ 语言链接的函数的指针,调用该函数时,具有与以下相同的效果:
对于非泛型 lambda,在闭包类型的默认构造实例上调用闭包类型的operator()。
对于泛型 lambda,在闭包类型的默认构造实例上调用泛型 lambda 相应的operator() 特化。
转换函数(模板)返回的值为
如果operator()是静态的,则为具有C++语言链接的指向该operator()的指针,
否则,指向具有 C++ 语言链接的函数的指针,在调用该函数时,具有与以下相同的效果:
对于非泛型 lambda,在闭包类型的默认构造实例上调用闭包类型的operator()。
对于泛型 lambda,在闭包类型的默认构造实例上调用泛型 lambda 相应的operator() 特化。
如果函数调用运算符为 constexpr,则此函数为 constexpr。
#include <iostream>
using namespace std;
auto Fwd = [](int(*fp)(int), auto a) { return fp(a); };
auto C = [](auto a) { return a; };
auto NC = [](auto a) { static int s; return a; };
int main() {
static_assert(Fwd(C, 3) == 3);
// static_assert(Fwd(NC, 3) == 3); // error: no specialization can be constexpr because of static s
}