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));
} // ^^^^^^^^^^^