Reputation: 1468
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...
Upvotes: 26
Views: 3596
Reputation: 509
In order to achieve the desired behavior with capture by reference, no generic lambda capture of C++14 is required (but as always with capture by reference, caution is required not to create a dangling reference):
template <typename T>
void func(T&& t) {
other_func([&t](int v) { return std::forward<T>(t) + v; });
}
On the contrary if the decision is made to use capture by value, the lambda should be marked as mutable to allow effective moving (because a const qualifier is implicitly added to lambdas):
template <typename T>
void func(T&& t) {
other_func([t = std::forward<T>(t)](int v) mutable { return std::move(t) + v; });
}
Upvotes: 4
Reputation: 42554
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;
});
}
Upvotes: 10
Reputation: 54589
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.
Upvotes: 10