玩命加载中 . . .

33-lambda表达式的完美转发


在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&&

那就把TT&&代入到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&&形参使用decltypestd::forward它们

文章作者: kunpeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kunpeng !
  目录