type of reference-captured object inside lambda
c++, decltype, lambda, language-lawyer
Solution
There is no special rule regarding non parenthesized applications of `decltype` (ie. [expr.prim.lambda]/20 does not apply). So we just fall back to the usual definition of `decltype`, which mandates that if the operand is an id-expression, the yielded type is just the declared type of the entity, and that's not a reference type. Hence VC++ is wrong.
NB: it doesn't matter whether `dict` is captured or not, because ¶17:
Every id-expression within the compound-statement of a lambda-expression that is an odr-use (3.2) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [ Note: An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. Furthermore, such an id-expression does not cause the implicit capture of the entity. — end note ]
`decltype` never odr-uses any of its operands or suboperands. This rule actually gets pretty problematic at times, e.g. as shown in core issue 958:
int f (int&);
void* f (const int&);
int main()
{
int i;
[=] ()-> decltype(f(i)) { return f(i); };
}
Here, `decltype(f(i))` uses the non-`const` `i` from the enclosing scope. However, since the lambda isn't `mutable`, the `i` in the body is actually `const`, hence the trailing-return-type is incorrect. CWG concluded this arises too infrequently to be worth solving.
Problem
The following code works with gcc ``` #include <map> int main() { std::map<int, double> dict; const auto lambda = [&]() { decltype(dict)::value_type bar; }; } ``` But for msvc I have to additionally use `std::remove_reference` ``` #include <map> #include <type_traits> int main() { std::map<int, double> dict; const auto lambda = [&]() { std::remove_reference_t<decltype(dict)>::value_type bar; }; } ``` Otherwise I get an error: ``` error C2651: 'std::map<int,double,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> &': left of '::' must be a class, struct or union ``` Which compiler shows the correct behaviour according to the standard? update: For msvc `decltype(dict)` really is a reference, as the following code ``` #include <map> int main() { std::map<int, double> dict; const auto lambda = [&]() { decltype(dict) foo; }; } ``` errors with ``` error C2530: 'foo': references must be initialized ``` If this really is wrong behaviour, it could lead to nasty bugs, like dangling references when code is compiled with msvc. ``` #include <map> std::map<int, double> return_a_map() { std::map<int, double> result; return result; } int main() { std::map<int, double> dict; const auto lambda = [&]() { decltype(dict) foo = return_a_map(); // foo is a dangling reference in msvc }; } ```