C++ Lambda表达式第一篇, 闭合(Closuretype)-ClosureType::operator(params)

时间:2024-07-07 09:59:03
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*
        };
    }
};