Why does capturing by reference in lambda not change the type of variables?

I thought that capturing by reference changes the types of variables. Consider the following example:

#include <cassert> #include <type_traits> int main() { int x = 0; int& x_ref = x; const int x_const = x; const int& x_const_ref = x_const; auto lambda = [&]() { static_assert(std::is_same<decltype(x), int>::value, "!"); static_assert(std::is_same<decltype(x_ref), int&>::value, "!"); static_assert(std::is_same<decltype(x_const), const int>::value, "!"); static_assert(std::is_same<decltype(x_const_ref), const int&>::value, "!"); }; lambda(); } 

None of the checks succeeded, so the type of the source variables is preserved. So how efficient is link capture? I thought that if the user commits by reference, new variables with the same name are introduced into the local scope in order to have a reference type of source variables. But it seems that this is not so.

Question: What is the motivation for this? Or is this something that does not understand me?

+7
c ++ lambda c ++ 14
source share
2 answers

I thought that if a user captures by reference, new variables of the same name are introduced into the local scope in order to have a reference type of source variables.

The standard says (my attention):

An object is captured by reference if it is implicitly or explicitly captured, but not captured by the copy. It is not indicated whether declared additional elements of a non-static element are declared in the closure type for objects captured by the link . If declared, such non-static data elements must be of letter type.

Therefore, your expectations were wrong. Capturing by reference does not mean creating a local reference to the object in the external area.


As a note, note the following:

 int x; auto l = [&x](){ return [&x](){} }(); 

If your expectations were correct, x would be a link to a link in lambda l , this is something that does not exist in C ++.

+3
source share

Let's start with something simple:

 int x = 0; auto lambda = [=]{ return x; }; 

What does return x do there? The "obvious" answer is that it returns the value of the lambda member variable named x . But actually it is not. The lambda does not have a member named x - the lambda data members themselves are unnamed. What happens is that:

Each id-expression inside the compound lambda-expression operator, which is unacceptable (3.2) of the object captured by the copy, is converted to access the corresponding unnamed data element of the closure type.

When you use x , you are actually referring to the corresponding member variable. Thus, the above expression can be interpreted as:

 struct __unnamed { int _1; __unnamed(int i) : _1(i) { } void operator=(__unnamed const&) = delete; int operator() const { return _1; } }; __unnamed lambda(x); 

Why I say this, there is only one x . Variable int x . So when you do decltype(x) , it gives you decl ared type x . This is of course int . This does not change if you capture by reference, because capturing your lambda does not change the types of variables that you captured.


Now, if you want to access those unnamed lambda members, you can do it. But instead of decltype(x) you want decltype((x)) :

Each occurrence of decltype((x)) , where x is, possibly, a parenthesized id expression that names an entity with automatic storage duration, is processed as if x were converted to access the corresponding data item of the closure type, would be declared if x was odr-using the designated object.

That is, in your example decltype(x) is int and decltype(x_const) is int const ... decltype((x)) is int& and decltype((x_const)) is int const& . This is probably more than what you really were looking for. Because now we are actually referring to lambda members, or at least as if we were. But note that the usual decltype((id)) rules still apply, so you'll always get reference types for lvalues.

+4
source share

All Articles