在lambda的形参中可以使用auto关键字就可以使用泛型lambda,这样在闭包类中的operator()函数是一个函数模版
auto f = [](auto x){ return func(normalize(x)); };
对应的闭包类中的函数调用操作符看来就变成这样:
class SomeCompilerGeneratedClassName {
public:
template<typename T>
auto operator()(T x) const
{ return func(normalize(x)); }
};
这里需要将参数进行转发,所以需要用到通用引用+完美转发
auto f = [](auto&& x)
{ return func(normalize(std::forward<???>(x))); };
现在的问题是std::forward的模板参数填什么
如果是函数模板,可以填T,但现在没有T,现在能用的就是x,考虑获取x的类型
传递给lambda的是一个左值,decltype(x)就能产生一个左值引用;如果传递的是一个右值,decltype(x)就会产生右值引用
在模板函数的通用引用中,如果传左值,转发时std::forward的参数是T&,如果是右值,转发时std::forward的参数是T
现在的泛型lambda中,如果x绑定的是一个左值,decltype(x)就能产生一个左值引用,跟之前一样。然而如果x绑定的是一个右值,decltype(x)就会产生右值引用,而不是常规的非引用,也就是现在传给std::forward的参数是T&&
那就把T和T&&代入到std::forward中看看有什么不同
template<typename T>
T&& forward(remove_reference_t<T>& param)
{
return static_cast<T&&>(param);
}
如果用户想要完美转发一个Widget类型的右值时,它会使用Widget类型(即非引用类型)来实例化std::forward,然后产生以下的函数:
Widget&& forward(Widget& param) //当T是Widget时的std::forward实例
{
return static_cast<Widget&&>(param);
}
将T换成Widget&&会如何
Widget&& && forward(Widget& param) //当T是Widget&&时的std::forward实例
{ //(引用折叠之前)
return static_cast<Widget&& &&>(param);
}
引用折叠后
Widget&& forward(Widget& param) //当T是Widget&&时的std::forward实例
{ //(引用折叠之后)
return static_cast<Widget&&>(param);
}
对比这个实例和用Widget设置T去实例化产生的结果,它们完全相同。表明用右值引用类型和用非引用类型去初始化std::forward产生的相同的结果
所以无论是左值还是右值,把decltype(x)传递给std::forward都能得到我们想要的结果,因此lambda的完美转发可以写成:
auto f =
[](auto&& param)
{
return func(normalize(std::forward<decltype(param)>(param)));
};
也支持可变形参
auto f =
[](auto&&... params)
{
return func(normalize(std::forward<decltype(params)>(params)...));
};
请记住:
- 对
auto&&形参使用decltype以std::forward它们