Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capture by universal reference

Tags:

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...

like image 637
Richard Vock Avatar asked Jan 20 '14 15:01

Richard Vock


People also ask

What is a universal reference?

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.

What is auto && in C++?

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.

What is reference collapsing?

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.

What is forwarding reference in C++?

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.


2 Answers

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;   }); } 
like image 53
Casey Avatar answered Oct 15 '22 02:10

Casey


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.

like image 35
ComicSansMS Avatar answered Oct 15 '22 01:10

ComicSansMS