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.