在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
它们