This page looks best with JavaScript enabled

[EMCpp]Item-33 Use decltype on auto&& parameters to std::forward them

 ·  ☕ 2 min read · 👀... views

C++14 introduces generic lambdas, which use auto in their parameter specifications.

1
auto f = [](auto x){ return normalize(x); };

If we want to perfect-fowrad a parameter x to normalize, we make two changes:

  1. use universal reference auto&&
  2. apply decltype on parameter to specify the correct type for type argument of std::forward
1
auto f = [](auto&& x){ return normalize(std::forward<decltype(x)>(x)); };

As a fact of decltype(x):

  • if x is bound to an lvalue, it will yield an lvalue refernece;
  • if x is bound to an rvalue, it will yield an rvalue reference

Actually, the result of decltype(x) doesn’t follow the convention of std::forward, where it dictates that the type argument be an lvalue reference to indicate an lvalue and a non-reference to indicate an rvalue.

Thanks to reference-collapsing rule, even though rvalue convention is broken here, the collapsing result is still the same. Say the T in the implementation of std::forward below is instantiated as Widget&&, an rvalue reference type:

1
2
3
4
template<typename T>                         // in namespace
T&& forward(remove_reference_t<T>& param)    // std
{  return static_cast<T&&>(param);
}

and we get this before reference collapsing:

1
2
3
4
Widget&& && forward(Widget& param)         // instantiation of
{                                          // std::forward when 
    static_cast<Widget&& &&>(param);       // T is Widget&& 
}

After reference collapsing:

1
2
3
4
Widget&& forward(Widget& param)         // instantiation of
{                                       // std::forward when 
    static_cast<Widget&&>(param);       // T is Widget&& 
}

This is exactly what we expect.

Variadic parameters

For more than a single parameter, using following format:

1
2
auto f = [](auto&&... xs)
         { return normalize(std::forward<decltype(xs)>(xs)...); }
Share on
Support the author with