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
};
// ...
}