Search code examples
c++c++11boost-bind

Functors vs. std::bind


Sometimes I tend to write functors, not for the sake of maintaining state between function calls, but because I want to capture some arguments that are shared between function calls. As an example:

class SuperComplexAlgorithm
{
    public:
        SuperComplexAlgorithm( unsigned int x, unsigned int y, unsigned int z )
            : x_( x ), y_( y ), z_( z )
        {}

        unsigned int operator()( unsigned int arg ) const /* yes, const! */
        {
            return x_ * arg * arg + y_ * arg + z_;
        }

    private:
        // Lots of parameters are stored as member variables.
        unsigned int x_, y_, z_;
};

// At the call site:
SuperComplexAlgorithm a( 3, 4, 5 );
for( unsigned int i = 0; i < 100; ++i )
    do_stuff_with( a ); // or whatever

Compared to

unsigned int superComplexAlgorithm( unsigned int x, unsigned int y,
                                    unsigned int z, unsigned int arg )
{
    return x * arg * arg + y * arg + z;
}

// At the call site:
auto a = std::bind( superComplexAlgorithm, 3, 4, 5, std::placeholders::_1 );
for( unsigned int i = 0; i < 100; ++i )
    do_stuff_with( a );

the first solution has a lot of drawbacks, in my opinion:

  • Documentation of what x, y, z do is split at different places (constructor, class definition, operator()).
  • Lots of boiler-plate code.
  • Less flexible. What if I want to capture a different sub-set of parameters?

Yes, I just realized how useful boost::bind or std::bind can be. Now for my question before I start using this in a lot of my code: Is there any situation where I should consider using a hand-written stateless functor over binding arguments in a plain function?


Solution

  • The lambda solution would be the idomatic C++11 way:

    auto a = [&]( int x ){ return superComplexAlgorithm( 3, 4, 5, x ); };
    for( unsigned int i = 0; i < 100; ++i )
      do_stuff_with( a );
    

    The advantage of using a hand written functor is that you can:

    1. Move arguments into the capture, or transform them (you can do that with lambda in C++1y, but not yet -- this is also possible with bind)
    2. It has a non-anonymous type (so you can talk about the type without using auto) -- the same is true of bind, but it also has a non-anonymous meaningful type you can get at without decltypeing the entire expression! C++1y again makes this better for bind/lambda.
    3. You can do perfect forwarding of the remaining arguments (you might be able to do this in C++1y with a lambda, and bind does this)
    4. You can mess around with how the captured data is stored (in a pointer? a smart pointer?) without messing up the code where you create the instance.

    You can also do some extremely powerful things with hand written functors that are impossible to do with bind and lambdas, like wrap an overload set of multiple functions into one object (that may or may not share a name), but that kind of corner case isn't going to show up often.