Functors vs std :: bind

Sometimes I prefer to write functors, not to maintain 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 with

 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 many disadvantages, in my opinion:

  • Documentation that x , y , z do is split into different places (constructor, class definition, operator() ).
  • A lot of boiler room code.
  • Less flexible. What if I want to capture another subset of parameters?

Yes, I just realized how useful boost::bind or std::bind . Now for my question, before I start using this in many of my codes: Is there a situation where I should consider using a hand-written functional without saving the binding arguments in a simple function?

+4
source share
2 answers

A lambda solution would be the C ++ 11 idiom 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 written functor is that you can:

  • Move arguments to capture or convert them (you can do it with lambda in C ++ 1y, but not yet - it's also possible with bind )
  • It has a non-anonymous type (so you can talk about a type without using auto ) - the same goes for bind , but it also has a non-anonymous meaningful type, which you can do without decltype entire expression! C ++ 1y does it better again for bind / lambda.
  • You can do an excellent forward of the remaining arguments (you could do it in C ++ 1y using lambda, and bind does it)
  • You can get around how the captured data is stored (in the index? Smart pointer?) Without messing up the code in which you create the instance.

You can also do some extremely powerful things with hand-written functions that cannot be done with bind and lambdas, for example, transfer many overloads from several functions to one object (which may or may not share the name), but this kind of angle will not appear frequently.

+8
source

Is there any situation where I should consider using handwritten functionality without storing binding arguments in a simple function?

From the comments:

I do not want to define superComplexAlgorithm on the call site, because it is super complex, 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 number of arguments, then you might be better off with a class that provides a general state in the internal implementation of the function. Another thing is that this angular case, the behavior of std::bind will be equivalent to the functor you provided in the question.

Some notes as you mention boost::bind and std::bind as alternatives. You might want to check what your implementation does. For example, with boost 1.35 (ancient), the bind operation will make 4 copies of each argument, VS2010 will make 7 copies in std::bind , although std::bind in gcc 4.7 did only 1. If the arguments are small, then to a high cost, but if you perform an operation or your road objects to copy ... just measure.

For lambda, if performance is a problem, also measure how it behaves. He must make one copy. If performance is not an issue, then the lambda is much less clear about what is happening (the user must read the lambda implementation to understand this), and even looking at the code, it may not be so obvious.

+4
source

Source: https://habr.com/ru/post/1492941/


All Articles