在 C++14 中新增泛型lambda时,是使用基于 auto 的语法,比较简单但也带来一定的局限性,因此在 C++20 中进行改进,新增模板语法的泛型 lambda 。让我们先回顾一下基于 auto 语法的泛型 lambda ,以及其局限性,然后再看 C++20 新增的基于模板的泛型 lambda 。
(C++14)基于 auto 的泛型 lambda
C++14 中的泛型 lambda ,是使用 lambda 参数中的 auto 关键字来表示不同的类型,一个例子如下:
#include
#include
using std::cout, std::endl, std::string;
int main( int argc, char * argv[] )
{
auto f1 = [] ( auto x, auto y ) { return x + y; };
cout << &f1 << endl;
string (*fp1)( string, string ) = f1;
double (*fp2)( double, int ) = f1;
cout << (void *)fp1 << " " << (void *)fp2 << endl;
double a1 = f1( 2, 5.0 );
string a2 = fp1( "2", "5.0" );
cout << a1 << " " << a2 << endl;
return 0;
}
上述代码编译和执行的结果为:
[smlc@test code]$ g++ -std=c++20 a12.cpp
[smlc@test code]$ ./a.out
0x7ffcef96de2f
0x401f18 0x401f4b
7 25.0
由于 lambda 表达式实际定义的是一个闭包类(ClosureType),通过重载 operator () 来提供仿函数的调用。通过 auto 关键字定义的泛型 lambda ,相当于如下的定义:
struct {
template
auto operator() ( T x, U y ) const { return x + y; }
};
基于 auto 的泛型 lambda 的局限性
基于 auto 的泛型 lambda ,最主要的局限性,是 auto 描述是整个类型,而不能像模板一样细化类型的模式。例如如果想 lambda 的参数类型是某种类型的数组(std::vector
template struct is_std_vector : std::false_type { };
template struct is_std_vector<std::vector> : std::true_type { };
auto f = [ ] ( auto vecType ) {
static_assert( is_std_vector< decltype vectype>::value, "" ); // <1> 额外的判断std::vector
using T = typename decltype( vecType )::value_type; // <2> 使用其他手段获取数组的具体类型
// ... other code ...
};
其他还有很多类似的需要细化类型的情况,例如多个参数之间的类型有关联,或者是要去掉类型 const 等附加信息,这些都需要通过 decltype 来获取类型然后再进行各种处理,反而使代码更复杂了,因此增加模板语法的 lambda 是有必要的。
(C++20)新增基于模板的 lambda 语法
C++20中新增了基于模板的lambda语法,对于上面的情况可以更简洁的描述,和其他模板的语法也保持一致:
auto f = [ ] < typename t> ( std::vector vecType ) { // <1> 无需额外判断std::vector,具体数组类型也可以直接使用T
// ... other code ...
}
语法上比较简单,不需要额外的 template 关键字,直接在 “ [ ] ” 和 “ ( ... ) ” 之间增加 “ < ...> ” 表示模板参数,然后在 lambda 参数和函数体中,就可以使用模板参数中描述的类型了。
基于模板的lambda也可以同时使用auto类型的参数,但不建议这样使用,混合使用这两种方式并不能带来额外的好处,反而使代码更加难懂。既然已经使用了模板,那么auto类型的参数也改为模板参数类型好了。
【往期回顾】