Bernard
Bernard

Reputation: 5666

Must constexpr expressions be captured by a lambda in C++?

Here is a piece of code that won't compile in MSVC 2015 (ignore the uninitialized value access):

#include <array>
int main() {
    constexpr int x = 5;
    auto func = []() {
        std::array<int, x> arr;
        return arr[0];
    };
    func();
}

It complains that:

'x' cannot be implicitly captured because no default capture mode has been specified

But x is a constexpr! x is known at compile time to be 5. Why does MSVC kick up a fuss about this? (Is it yet another MSVC bug?) GCC will happily compile it.

Upvotes: 16

Views: 1561

Answers (2)

Barry
Barry

Reputation: 302767

The code is well-formed. The rule from [expr.prim.lambda] is:

If a lambda-expression or an instantiation of the function call operator template of a generic lambda odr-uses (3.2) this or a variable with automatic storage duration from its reaching scope, that entity shall be captured by the lambda-expression.

Any variable that is odr-used must be captured. Is x odr-used in the lambda-expression? No, it is not. The rule from [basic.def.odr] is:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).

x is only used in a context where we apply the lvalue-to-rvalue conversion and end up with a constant expression, so it is not odr-used, so we do not need to capture it. The program is fine. This is the same idea as why this example from the standard is well-formed:

void f(int, const int (&)[2] = {}) { }   // #1
void f(const int&, const int (&)[1]) { } // #2

void test() {
    const int x = 17;
    auto g = [](auto a) {
        f(x); // OK: calls #1, does not capture x
    };
    // ...
}

Upvotes: 14

Sam Varshavchik
Sam Varshavchik

Reputation: 118300

Even though x is a constexpr, it is no different from any other object, otherwise, and follows the same rules with regards to scoping. There are no exceptions to scoping rules for constexprs, and a lambda must be coded to explicitly capture it.

Upvotes: -2

Related Questions