The (not so) surprising behavior of std::bind
June 4, 2013
Often when I am unit testing a method which accepts a function pointer, I first write a simple test to verify that the point-to-function is called. The C++11 standard has added std::bind
to easily create function pointers. When I used std::bind
in a recent project, I discovered what I thought was surprising behavior. After a bit more investigation, I found that the behavior is not so surprising at all, and in fact provides some useful flexibility.
To Copy or Not to Copy
I’ve recently been writing some code which implements a simple map-reduce algorithm using MPI. The constructor for the map-reduce implementation class is defined like this:
The first thing I would expect the map()
method to do is call the provided partitioning method to partition the range of iterators. So I wrote a simple class to allow a unit test to determine if the partitioning method was called.
Then in the unit test, I used std::bind
to bind the to partition
member function like this:
To my surprise, I found that the partitioning method was never called. At least, the GetPartitioningMethodCalled()
method always returned false. It wasn’t until I added a copy constructor to the PartitioningTracker
class that I discovered the problem.
With Great Power Comes Great Responsibility
This answer on Stack Overflow helped to determine the cause of this behavior. It turns out that std::bind
has a number of overloads. The one I chose made a copy of the tracker object. So although my code in the map()
method was indeed calling the partitioning method, it was calling the method on an instance of PartitionTracker
which existed solely for the purpose of the std::bind
call, not on the instance of PartitionTracker
I had created. When my unit test asserted that GetPartitioningMethodCalled()
returned true, it failed!
Changing the test code to pass the address of the locally created PartitionTracker
instance solved the problem.
The Full Story
To get a better idea of how the various overloads of std::bind
work, I wrote a simple test program.
This program will output the following:
Bound to copy of tracker Method called _methodCalled value: false Bound to local instance of tracker Method called _methodCalled value: true Bound to refrence to tracker Method called _methodCalled value: true
In all three cases, the method is called as expected, but only the final two cases call the method on the local instance of Tracker
class. You can see the code execute on Ideone here.
In hindsight, this behavior should not have been surprising. C++ uses value semantics to pass arguments by default, so it should have been clear why my unit test was not working as expected initially. In fact, this behavior provides maximum flexibility, allowing the caller of std::bind
to use it as he or she pleases.