Perfect forwarding in a lambda?

My favorite idiom for this is:

auto f = [](auto&& x){myfunction(decltype(x)(x));}

which I read as "x as the type x was declared as".

To see how this works, examine what happens when x is an int&&. decltype(x)(x) is (int&&)(x), which produces an rvalue reference to x. If x is an int&, then we get (int&)(x) which is a noop cast to a reference. Remember, decltype(x) includes the reference category.

Now, for auto&& parameters this is shorter but equivalent to:

auto f = [](auto&& x){myfunction(std::forward<decltype(x)>(x));}

the alternative.

For auto parameters:

auto f = [](auto x){myfunction(decltype(x)(x));}

it induces an extra copy, while

auto f = [](auto x){myfunction(std::forward<decltype(x)>(x));}

instead moves-from x.

While I usually treat C-style casts as being too dangerous, decltype(x)(x) can at worst make a type-correct copy of x if x is not an auto&& variable. And there is something to be said for the brevity of it.


With C++20, you can now specify a template parameter list for a lambda. The following was taken directly from https://en.cppreference.com/w/cpp/language/lambda

auto f = []<typename ...Ts>(Ts&& ...ts) {
    return foo(std::forward<Ts>(ts)...);
};

The canonical way to forward a lambda argument that was bound to a forwarding reference is indeed with decltype:

auto f = [](auto&& x){
  myfunction(std::forward<decltype(x)>(x));
} //                      ^^^^^^^^^^^