That's what's called a Generalized Lambda Capture, and yes, it's C++14.
Basically it allows you to create a new variable as part of the capture list.
Text from link:
In C++11, lambdas could not (easily) capture by move. In C++14, we have
generalized lambda capture that solves not only that problem, but
allows you to define arbitrary new local variables in the lambda
object. For example:
auto u = make_unique<some_type>( some, parameters ); // a unique_ptr is move-only
go.run( [ u=move(u) ] { do_something_with( u ); } ); //move the unique_ptr into the lambda
In the above example, we kept the
name of the variable u
the same inside the lambda. But we’re not
limited to that… we can rename variables:
go.run( [ u2=move(u) ] { do_something_with( u2 ); } ); // capture as "u2"
And we can add arbitrary new state to the lambda object, because
each capture creates a new type-deduced local variable inside the
lambda:
int x = 4;
int z = [&r = x, y = x+1] {
r += 2; // set x to 6; "R is for Renamed Ref"
return y+2; // return 7 to initialize z
}(); // invoke lambda
In your specific instance, you have a lambda that is returning a lambda. The nested lambda is capturing f
(which was only a parameter in the parent lambda) by using this new syntax.