Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is decltype(x) inside a lambda supposed to return when x is captured by reference? [duplicate]

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
    };
}
like image 348
Tobias Hermann Avatar asked Oct 19 '16 12:10

Tobias Hermann


People also ask

What does decltype stand for?

Decltype stands for declared type of an entity or the type of an expression. It lets you extract the type from the variable so decltype is sort of an operator that evaluates the type of passed expression. SYNTAX : decltype( expression ) C++

What is decltype used for?

The decltype type specifier yields the type of a specified expression. The decltype type specifier, together with the auto keyword, is useful primarily to developers who write template libraries. Use auto and decltype to declare a function template whose return type depends on the types of its template arguments.

What is a lambda capture?

A capture clause of lambda definition is used to specify which variables are captured and whether they are captured by reference or by value. An empty capture closure [ ], indicates that no variables are used by lambda which means it can only access variables that are local to it.

Which of the following operator is used to capture all the external variable by reference?

5. Which of the following operator is used to capture all the external variable by reference? Explanation: The lambda expression uses & operator to capture the external variable by reference.


1 Answers

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.

like image 119
Columbo Avatar answered Oct 19 '22 18:10

Columbo