Yes, this program has undefined behavior. The result of a lambda expression is an rvalue of unnamed class type [expr.prim.lambda.closure]/1 that has an overloaded function call operator [expr.prim.lambda.closure]/3. Thus, what you're essentially doing in your example here
(*(T*) nullptr) ()
is you're invoking the operator ()
of your lambda type (which is a non-static member function) on an object that does not exist. You can find more details on why this is UB here: When does invoking a member function on a null instance result in undefined behavior?
While you definitely are invoking undefined behavior, this undefined behavior will almost certainly not manifest itself in a crash in your particular example. The reason for this is simple: the body of your lambda does not access any members of the class, so even if that operator ()
is called with a nullptr
for this
, it doesn't ever attempt to access any memory based off that pointer. The type of the lambda does not even have any members since your lambda does not capture anything. Furthermore, in an optimized build, one would expect the compiler to simply optimize away x
from this example program since nothing about it is observable behavior…
If you make your var
template constexpr
, the compiler will refuse to compile it because the expression used to initialize the variable is not a constant expression because it invokes undefined behavior[expr.const]/2.6…
If we modify your example a bit such that the lambda has a member and we access that member in the lambda body
template<typename T>
int getvar (const T&)
{
return (*(T*) nullptr) ();
}
int main ()
{
return getvar ([x = 42]() { return x; });
}
you will very likely end up with a program that actually crashes…