Why does decltype(captured_var) not behave as expected?
Asked Answered
T

1

5
#include <type_traits>

int x = 0;
void f(int const x)
{
    static_assert(std::is_const_v<decltype(x)>); // ok
}

int main() 
{
    int n = 0;
    [n, m = n]
    {
        static_assert(std::is_const_v<decltype(m)>); // ok
        static_assert(std::is_const_v<decltype(n)>); // err
    };
}

See online demo

Why does decltype(captured_var) not behave as expected?

Thursday answered 26/6, 2021 at 13:6 Comment(1)
Because all captured variables are const by default if the lambda is not declared as mutable. Say m here, is const as expected.Thursday
C
6

Note that in decltype(n), n refers to the local variable n defined in main(), but not to the member of the closure type (as you expected).

[expr.prim.lambda.capture]/11

(emphasis mine)

Every id-expression within the compound-statement of a lambda-expression that is an odr-use of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.

[Note 7: An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. However, such an id-expression can still cause the implicit capture of the entity. — end note]


BTW: Gcc seems declaring the data member as const directly for non-mutable lambdas. That's why it gives const int for decltype(m). According to the standard, [expr.prim.lambda.capture]/10.2

(emphasis mine)

The type of such a data member is the referenced type if the entity is a reference to an object, an lvalue reference to the referenced function type if the entity is a reference to a function, or the type of the corresponding captured entity otherwise.

I think gcc is wrong; decltype(m) should lead to type int. Anyway, decltype(n) does refer to the local variable n, you can confirm that by e.g. changing n's type to int&&.

Gcc LIVE
Clang LIVE

Cogon answered 26/6, 2021 at 13:14 Comment(7)
clang++` says both n and m are non-const. Bug?Tephra
@TedLyngmo Because m is declared as int then decltype(m) leads to int? Even in a const-qualified member function. godbolt.org/z/G3T8e8qTGCogon
@Cogon If that's the case, wouldn't that imply that decltype(<any lambda capture>) should not be const, since that would have parity with member variables under a const context? Is this a specification problem in the standard? This actually appears more like a gcc bug to me now, since decltype(m) being const deviates from equivalent class behaviorRaoul
Interesting, we're seeing different behavior with different standards and/or versions of clang. I'm seeing that m and n are both non-const (like Ted saw), but they're both also non-mutable in the context of the non-mutable lambda. Curiouser and curiouser.Sammons
@Human-Compiler Gcc seems declaring the data member as const directly for non-mutable lambdas. I checked the standard, it says the type of data member should be the type of the corresponding captured entity, so I think gcc is wrong too.Cogon
MSVC appears to give the same output as clang (no const for either members in a const context or lambda capture). gcc appears to be the only one that treats the lambda capture as const (at least in terms of the 3 major compiler vendors). I still can't quite tell if this is an ambiguity in the standard for whether it can be const, or whether it's just a bug in gcc.Raoul
@Human-Compiler The standard says that the operator() should be const-qualified for non-mutable lambdas, but declaring the data member as const directly seems overdoing...Cogon

© 2022 - 2024 — McMap. All rights reserved.