Search code examples
c++lambdalanguage-lawyercl

local constexpr implicit capture failure in nested lambda failing to compile on msvc


While working on this question, a possible answer implied nested lambdas where the inner ones implicitly captured a constexpr local variable of the outer one. Here is a mre:

int main() {
    []() {
        // must be declared static with msvc
        constexpr int i = []() { return 42; }();
        []() { static_assert(i == 42); }();
    }();
    return 0;
}

LIVE gcc and clang are fine with that but msvc issues error C3493: 'i' cannot be implicitly captured because no default capture mode has been specified.
msvc only accepts the code if the variable is declared static.

What compilers are right and why?


Solution

  • i in i == 42 is not an odr-use per [basic.def.odr]/5.2.

    Informally this is because in the expression you only use the value of i, not its identity as an object, and because the variable is usable in constant expressions, i.e. its value is compile-time known (because the variable is declared constexpr).

    More specifically, you immediately apply a lvalue-to-rvalue conversion to i for the built-in == operator which expects prvalue operands, as required for [basic.def.odr]/5.2 to apply. (It would be different if == resolved to an overloaded operator== which takes arguments by-reference.)

    Because there is no odr-use of i, there is no need for it to be captured.