This is the last post in this series, and I am going to use it to wrap up a few things that didn’t quite fit in anywhere else.
As I said at the start, this series is not the ultimate guide to std::bind and lambda, it has focused on a few things that I find interesting. As always, if you want definitive information look at these three references:
As you’d expect, Herb Sutter has written and spoken about lambdas many times.
Passing values by reference
All of my examples have assumed that bound or captured arguments are captured by value. Take a look at this code:
double add( int i, float f )
{
return i + f;
}
typedef std::function< double( int ) > SingleArgumentAdd;
float f( 10.0f );
SingleArgumentAdd fn0( std::bind( add, _1, f ) );
SingleArgumentAdd fn1( [ f ]( int i )
{
return add( i, f );
} );
f = 20.0;
std::cout << fn0( 5 ) << ", ";
std::cout << fn1( 5 ) << "\n";
It writes out 15, 15
showing that the value of f
that is used is the value when the function object is created.
We could pass in a pointer to f
rather than f
itself:
double add1( int i, float* pF )
{
return i + *pF;
}
float f( 10.0f );
float* pF( &f );
SingleArgumentAdd fn0( std::bind( add1, _1, pF ) );
SingleArgumentAdd fn1( [ pF ]( int i )
{
return add1( i, pF );
} );
f = 20.0;
std::cout << fn0( 5 ) << ", ";
std::cout << fn1( 5 ) << "\n";
This time we get 25, 25
- the pointer is not dereferenced until fn0
and fn1
are called.
What about passing f
by reference? That should give us the same result as passing by pointer - the value of f
is the value when the function is called. Here's the code:
float f( 10.0f );
SingleArgumentAdd fn0( std::bind( add, _1, std::cref( f ) ) );
SingleArgumentAdd fn1( [ &f ]( int i )
{
return add( i, f );
} );
f = 20.0;
std::cout << fn0( 5 ) << ", ";
std::cout << fn1( 5 ) << "\n";
The code prints out 25, 25
, just as we'd expect. We had to do two things differently though:
- We have to explicitly tell std::bind that
f
must be passed by reference (a const reference in this case). If we don't do this, std::bind will dereference f
and use whatever value is in f
when std::bind is called. std::cref is a function that returns an object that wraps the reference and makes sure that the reference is not dereferenced until fn0
and fn1
are called. See The C++ Programming Language, 4th Edition, section 33.5.1 for more information.
- Lambdas are much simpler. We write
&f
in the capture block to indicate that f
should be captured by reference.
Whether we are using pointers, references, lambdas or std::bind there is one thing we need to keep in mind - when we dereference the reference, the object it refers to must still exist.
Nesting and chaining functions
When we have an object of type std::function it is a first class object in C++. We can copy it, pass it to functions, return it from functions, store it as a member variable and treat it like any other object.
This means that we can set up nested functions, we can chain functions together and we can use std::function objects for callbacks. As always, just because you can doesn't mean that you should. I am experimenting with chains of functions for my current project and, while I can make it work, it isn't pretty and it isn't clear what is going on.
operator ()
should be const for predicates
I am not going to go into this in detail because others have done a better job than I have. In general, when you write your own predicate function object, operator ()
should be const
- it cannot update any stored state in the function object. There are two reasons for this:
- The standard does not place any restrictions on the number of times a predicate is copied (I think that std::for_each is the one exception). As Josuttis points out in The C++ Standard Library: A Tutorial and Reference (2nd Edition) section 10.1.4, a typical implementation of std::remove_if does copy the predicate and will give the wrong result if the predicate is storing state.
- By default, there is no guarantee that the algorithm will traverse the list from the beginning to the end. As far as I can tell, these are the only algorithms which are guaranteed to traverse from beginning to end: std::for_each, std::copy, std::move, std::accumulate, std::inner_product, std::partial_sum and std::adjacent_difference. I found these by searching the standard for the phrases starting from first and proceeding to last, and in order.
By default, the function object constructed by a lambda expression has a const
operator ()
. If you really want it to be non-const you can declare the lambda mutable
.
Why do we need std::find_if and std::count_if?
There are algorithms which have an overloaded predicate and non-predicate version, for example, std::is_sorted :
bool
is_sorted( ForwardIterator beg, ForwardIterator end );
bool
is_sorted( ForwardIterator beg, ForwardIterator end, BinaryPredicate op );
If we look at std::find we see why we can't overload it with a predicate and non-predicate version:
InputIterator
find( InputIterator beg, InputIterator end, const T& value );
Despite the fact that we can supply template specializations that will match a function, we can't know whether we are supplying a function because we are searching for a function in a container of functions or whether we are supplying a function to act as a predicate. That means we have to have a separate std::find_if (and std::count_if) function.
std::bind and member functions
In part 2 we looked at pointers to member functions, but I never demonstrated how these work with std::bind.
class MemberFunctionDemo
{
public:
double add( int i, float f )
{
return i + f;
}
MemberFunctionDemo()
{
std::cout << std::bind(
&MemberFunctionDemo::add,
this,
_1,
20.0f )( 10 );
}
};
The first argument to std::bind is always the function that will be called. For member functions we have to use &
to get a pointer to the function, and the function name must be fully qualified. The second argument is the object to call the function on - it can be a pointer or a reference to the object, in my example, I am using this
.
std::bind as a way of limiting scope
A colleague at Adobe pointed out that we can use a lambda to create a block of code that does not have access to all variables in the surrounding scope:
int i, j, k;
// Some code here...
[ i, j ](){
// Only have access to i & j here.
// No access to k
}();
I am not sure whether this is a Good Thing, a Bad Thing or just a Thing.