This is a follow up of the discussion found here.
The following code compiles both under gcc and clang (live demo). This is surprising for the case in line //1
Lambdas can "see" a lot of things in their surrounding scope without needing to capture them:
-- Global variables:
int x = 42;
int main() { []{ std::cout << x; }(); }
-- Static local variables:
int main() {
static int x = 42;
constexpr int y = 1337;
[]{ std::cout << x << y; }();
}
-- Functions:
int x() { return 42; }
int main() { []{ std::cout << x(); }(); }
-- Types:
using x = std::integral_constant<int, 42>;
int main() { []{ std::cout << x::value; }(); }
-- Local variables used in unevaluated contexts:
int main() {
int x = 42;
[]{ std::cout << sizeof(x); }();
}
This just naturally falls out of the language rules. You can do the same thing in C++98 with a hand-written callable object:
int main() {
int x = 42;
struct functor {
int operator()() const { return sizeof(x); }
};
std::cout << functor{}();
}
It's unsurprising, as sizeof
does not evaluate its expression:
int main() {
int x; // uninitialized
using y = std::integral_constant<size_t, sizeof(x)>; // x used in a constant expression
using z = std::integral_constant<size_t, 4>;
static_assert(std::is_same<y, z>::value, "");
std::cout << y::value;
}
The difference is the (lack of) evaluation of context. sizeof
is unevaluated.
As per N3337 (≈C++11)
§5.1 2 [expr.prim.lambda] / 11
If a lambda-expression has an associated capture-default and its compound-statement odr-uses
this
or a variable with automatic storage duration and the odr-used entity is not explicitly captured, then the odr-used entity is said to be implicitly captured;
and
§5.1.2 [expr.prim.lambda] / 12
If a lambda-expression odr-uses
this
or a variable with automatic storage duration from its reaching scope, that entity shall be captured by thelambda-expression
. If a lambda-expression captures an entity and that entity is not defined or captured in the immediately enclosing lambda expression or function, the program is ill-formed.
ODR use means use in potentially evaluated context:
§3.2 [basic.def.odr] / 2
An expression is potentially evaluated unless it is an unevaluated operand or a subexpression thereof. A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression and the lvalue-to-rvalue conversion is immediately applied
Since sizeof
isn't, and s
is in reaching scope of the lambda expression, it is okay. Returning s
means evaluating it, though, and that's why it's ill-formed.