4

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?

Markus Mayr
  • 4,038
  • 1
  • 20
  • 42
  • 9
    above all you should consider lambdas – stijn Jul 23 '13 at 12:20
  • @stijn: I don't want to define `superComplexAlgorithm` at the call site, because it is super complex, is used in many places, needs testing and documentation, etc.. I can't see how lambdas can be helpful in this situation, although I use them a lot in other situations. – Markus Mayr Jul 23 '13 at 12:23
  • 4
    @MarkusMayr, you can replace `std::bind` with a lambda. Instead of `std::bind( superComplexAlgorithm, 3, 4, 5, std::placeholders::_1 )`, you write `[](int x) { return superComplexAlgorithm(3, 4, 5, x); }`. – avakar Jul 23 '13 at 12:25
  • @avakar: Yes, that is doing exactly the same as `std::bind`, except that `std::bind` makes clear what my intentions were and the lambda expression does not (at least not at first glance). Even if you prefer lambdas in this case: Can you think of any situation where I should prefer a hand-written functor over one created with a lambda expression or `std::bind`? – Markus Mayr Jul 23 '13 at 12:30
  • 1
    Since lambdas became available to me, I haven't used `bind` or overloaded `operator()` even once. So no, I can't imagine such a situation :) Put the code into the lambda or write a function and call it from the lambda. – avakar Jul 23 '13 at 12:35
  • It's handy if you want to put more complex logic into `SuperComplexAlgorithm`. It allows you to easily add new methods that can handle some of the details without passing the arguments around and without introducing the names into a larger scope. – Vaughn Cato Jul 23 '13 at 12:42
  • 1
    I'd like to note that the "official" name of this pattern is [Currying](http://en.wikipedia.org/wiki/Currying). – leftaroundabout Jul 23 '13 at 12:48
  • @leftaroundabout, I'm not so sure it is. – avakar Jul 23 '13 at 12:55
  • @leftaroundabout Currying is technically the single-argument function chain with binding, not all partial-binding. – Yakk - Adam Nevraumont Jul 23 '13 at 13:14

2 Answers2

9

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    Why do you have a capture clause in your lambda? You shouldn't use captures unless you actually need to capture something. – Sebastian Redl Jul 23 '13 at 13:30
  • Why capture by reference if there is no capture at all? – David Rodríguez - dribeas Jul 23 '13 at 13:33
  • @sebastianredl It makes it similar to bind: usually the values you bind are not compile time constants, or you would not bother with `bind` or a lambda: a plain old function would do. And the runtime cost is zero if I am wrong, so I included it. – Yakk - Adam Nevraumont Jul 23 '13 at 13:44
  • 5
    If you want to emulate `bind`, use a copy capture. – Sebastian Redl Jul 23 '13 at 13:47
  • 1
    +1 @SebastianRedl: also, beware if you are *capturing* members – David Rodríguez - dribeas Jul 23 '13 at 14:22
  • You are *never* capturing members. In C++11, you can only capture objects with automatic storage duration: locals and function parameters. If you think you are capturing members, you are in fact implicitly capturing `this`. (I also dislike capture defaults, but that's really a style issue - they exceed my personal threshold for implicit behavior.) – Casey Jul 23 '13 at 15:07
4

Is there any situation where I should consider using a hand-written stateless functor over binding arguments in a plain function?

From the comments:

I don't want to define superComplexAlgorithm at the call site, because it is super complex, is used in many places, needs testing and documentation, etc..

If the implementation of superComplexAlgorithm requires a bunch of code and you end up splitting it into different functions that take a large amount of arguments, then you might be better off with a class that provides the shared state across the internal implementation functions. Other that that corner case, the behavior of std::bind would be equivalent to the functor you provided in the question.

Some notes, since you mention boost::bind and std::bind as alternatives. You might want to test what your implementation does. For example, with boost 1.35 (ancient) the bind operation would make 4 copies of each argument, VS2010 would make 7 copies in std::bind although std::bind in gcc 4.7 did only 1. If the arguments are small that won't make up to a high cost, but if you are doing the operation or your objects are expensive to copy... just measure.

Regarding the lambda, if performance is an issue, also measure how it behaves. It should make a single copy. If performance is not an issue, then a lambda is far less explicit on what is being captured (the user needs to read the implementation of the lambda to figure that out), and even looking at the code it might not be that obvious.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489