Let’s start with a quick reminder of what a lambda expression looks like:
std::vector< int > v_i( functionReturningVector() ); std::vector< double > v_d; float f = functionReturningFloat(); std::transform( std::begin( v_i ), std::end( v_i ), std::back_inserter( v_d ), [ f ]( int i ){ return i + f; } );
The lambda expression is this part:
[ f ]( int i ){
return i + f;
}
The standard says “Lambda expressions provide a concise way to create simple function objects”. The lambda expression we have just seen corresponds to the Adder
function object that we looked at earlier in this series:
class Adder { public: Adder( float f ) : f_( f ) {} double operator()( int i ) const { return i + f_; } private: float f_; };
Repeating what I said in this post:
The lambda function consists of three parts, contained in three different types of brackets. It starts with square brackets – [] – moves on to round brackets – () – then finishes off with curly brackets – {}.
The three parts correspond to the three essential features of the function object we created for solution #2:
- The value(s) passed into the constructor of the function object appear in the square brackets – []
- The value(s) passed into
operator ()
appear in the round brackets – ()- The code in
operator ()
appears in the curly brackets – {}
If we take our three options: a custom function object; std::bind wrapping a function and a lambda expression, we can find the commonality between them:
f
is the value passed in to the constructor of our function object.f
is the value bound by std::bind.f
is the value we place in the square brackets of the lambda (in standardese, the square brackets are called a lambda-capture,f
is a capture-list with a single element).i
is the value passed to the function call operator defined on the callable object. An object of typeAdder
is a callable object, the result of std::bind is a callable object and a lambda expression is a callable object.-
{ return i + f; }
is the code that we actually want to execute. For std::bind the code we want to execute has to be wrapped in a function, for a custom function object or a lambda we can just use the code directly.
If we want to, we can invoke the function call operator on a lambda directly:
float f = functionReturningFloat();
double d = [ f ]( int i )
{
return i + f;
}( 7 );
(we probably don’t want to, although I am sure somebody has already come up with a use for this)
A lambda-expression is an expression, and expressions have a type. We tried to work out what the type of the result of std::bind was last time – let’s try the same exercise with a lambda expression.
Section 5.1.2 of the standard talks about the type of a lambda expression. The type is a unique, unnamed nonunion class type. The standard tells us plenty about the properties of the type without ever revealing what the type actually is. As with std::bind I can force a compiler error and see what type the compiler is using. For Visual Studio I get this:
test_bl5_1::<lambda_d35a8a85fcf19231607c0773125e04ed>
For GCC I get this:
test_bl5_1()::__lambda1
(The lambda in question is declared in function test_bl5_1
)
As with the type of the return value of std::bind, we are clearly not meant to use these types deliberately – the standard is telling us so, they are different between different compilers, in the case of Visual Studio, the “type” isn’t really a type at all – it is some implementation magic, the GCC type starts with an underscore, and, in case I haven’t mentioned it enough, the standard tells us that we can’t use these types. Believe the standard – it’ll make life easier. This diversion into the actual types of std::bind and lambda has been exactly that – a diversion from our main purpose of actually using these things.
So we are in the same situation as we were with std::bind. We can’t use the type directly, we don’t know what it is. We do know what the properties of the type are – in particular we can apply operator ()
to objects of that type.
We can use the type deduction facilities of C++11 to handle lambdas without needing to know their type. We have already used a lambda with a templated function (std::transform) and auto
and decltype
work exactly as we would expect:
auto fn = [ f ]( int i ){ return i + f; };
typedef decltype( fn ) SingleArgumentAdd;
Last week we looked at std::function and saw that std::function can wrap a callable object. Since a lambda expression is a callable object, std::function can wrap a lambda expression:
typedef std::function< double( int ) > SingleArgumentAdd; SingleArgumentAdd fn; fn = [ f ]( int i ){ return i + f; }; std::cout << fn( 42 ); // And of course we can call it
If we need to save callable objects, std::function is the way to go. std::function can be the type of a member variable, it can be passed to and returned from functions. std::function is our all purpose "thing that supports operator ()
"
Finally, there is one thing that I have glossed over. When I was describing the commonality between std::bind, a function object and a lambda I looked at every variable and every type except one - the type of the result of invoking operator ()
.
If we look at the lambda we've been working with, we can see that if we remove the capture block (the bit inside square brackets) we have something that is very close to being a function declaration:
( int i ) { return i + f; };
It is missing the function name (which is what we'd expect - a lambda is an unnamed function), but it is also missing the return type. In this case, because the lambda consists of a single statement that is just a return we don't have to specify the return type, the compiler will deduce it for us - that gives us another place where the compiler does type deduction.
In our case, the result of i + f
is a double - that is exactly what we want. But what if we did need a return type, either because the type of the expression was not what we wanted, or because we had more complicated code within the lambda?
Lambdas use the new trailing-return-type syntax so if we wanted to be explicit about the function call operator returning a double
we would write this:
[ f ] ( int i ) -> double { return i + f; };
That's it for this post, and almost it for this series. I have one more post in mind which will tie up a few loose ends then I'll move on to something else.