When passing a deduced type as r-value reference I get universal reference functionality and can archieve perfect forwarding like this:
template <typename T> void func(T&& t) { other_func(std::forward<T>(t)); }
...due to the way T is derived and the standard's reference collapse rules.
Now consider other_func takes a function object
template <typename T> void func(T&& t) { other_func([](int v) { return t + v; }); // I chose addition for example purposes }
Now obviously this won't compile due to t not being captured. My question is: How do I capture it so the captured value will be whatever T is deduced to?
Is this possible using the new generic lambda captures? And if... how?
[t = std::forward<T>(t)] ?
I still don't really get the mechanics of the new capture initializers...
Universal reference was a term Scott Meyers coined to describe the concept of taking an rvalue reference to a cv-unqualified template parameter, which can then be deduced as either a value or an lvalue reference.
The auto && syntax uses two new features of C++11: The auto part lets the compiler deduce the type based on the context (the return value in this case). This is without any reference qualifications (allowing you to specify whether you want T , T & or T && for a deduced type T ). The && is the new move semantics.
Reference collapsing is the mechanism that leads to universal references (which are really just rvalue references in situations where reference-collapsing takes place) sometimes resolving to lvalue references and sometimes to rvalue references.
When t is a forwarding reference (a function argument that is declared as an rvalue reference to a cv-unqualified function template parameter), this overload forwards the argument to another function with the value category it had when passed to the calling function.
You can "capture by universal reference" in C++11, since the type of the template parameter T
is available to the lambda function (hideous live code example at Coliru):
template <typename T> void func(T&& t) { other_func([&t](int v) { return std::forward<T>(t) + v; }); }
Okay, let's try this. Unfortunately I don't have a compiler that supports this feature at hand, so forgive me if I gravely misinterpret things along the way.
The proposal covering this is N3648.
The interesting part here is that the type of the variable in the init capture is deduced as if using auto
:
The type of that member corresponds to the type of a hypothetical variable declaration of the form "auto init-capture ;" [...].
So the question of what you get from a capture list [c = std::forward<T>(t)]
is equivalent to what you get from a declaration auto c = std::forward<T>(t)
.
The deduced type here will be std::remove_reference<T>::type
(reference qualifiers are dropped by auto
), so you will always end up with a new value here. If t
was an rvalue reference, you will move-construct that new value, otherwise you will copy-construct (due to the return value of std::forward
).
The good thing is that this new value is owned by the lambda. So no matter what t
you passed in initially, it is safe to std::move
from the captured c
. So even though you do not know the type of the initial t
any longer you still didn't lose anything along the way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With