views:

467

answers:

5

I'm using C++ templates to pass in Strategy functors to change my function's behavior. It works fine. The functor I pass is a stateless class with no storage and it just overloads the () operator in the classic functor way.

template <typename Operation> int foo(int a) 
{
int b=Operation()(a);
/* use b here, etc */
}

I do this often, and it works well, and often I'm making templates with 6 or 7 templated functors passed in!

However I worry both about code elegance and also efficiency. The functor is stateless so I assume the Operation() constructor is free and the evaluation of the functor is just as efficient as an inlined function, but like all C++ programmers I always have some nagging doubt.

My second question is whether I could use an alternate functor approach.. one that does not override the () operator, but does everything in the constructor as a side effect! Something like:

struct Operation {
  Operation(int a, int &b) { b=a*a; }
};
template <typename Operation> int foo(int a) 
 {
   int b;
   Operation(a,b);
    /* use b here, etc */
 }

I've never seen anyone use a constructor as the "work" of a functor, but it seems like it should work. Is there any advantage? Any disadvantage? I do like the removal of the strange doubled parenthesis "Operator()(a)" , but that's likely just aesthetic.

+1  A: 
  1. Compilers actually inline the empty constructor of Operation (at least gcc in similar situations does, except when you turned off optimization)
  2. The disadvantage of doing everything in the constructor is that you cannot create a functor with some internal state this way - eg. functor for counting the number of elements satisfying a predicate. Also, using a method of a real object as a functor allows you to store the instance of it for later execution, something you cannot do with your constructor approach.
jpalecek
+2  A: 

Any disadvantage?

  • Ctors do not return any useful value -- cannot be used in chained calls (e.g. foo(bar()).
  • They can throw.
  • Design point of view -- ctors are object creation functions, not really meant to be workhorses.
dirkgently
Who can't throw?
Mykola Golubyev
@Mykola Golubyev: Dtors -- at least, you are not supposed to throw.
dirkgently
+1  A: 

From a performance pov the code demonstrated with get completely optimized with both VC and GCC. However, a better strategy often is to take the functor as a parameter, that way you get a lot more flexibility and identical performance characteristics.

A: 

I'd recommend defining functor that work with the STL-containers, i.e. they should implement operator(). (Following the API of the language you're using is always a good idea.)

That allow your algorithms to be very generic (pass in functions, functors, stl-bind, boost::function, boost::bind, boost::lambda, ...) which is what one usually wants.

This way, you don't need to specify the functor type as a template parameter, just construct an instance and pass it in:

my_algorithm(foo, bar, MyOperation())
Marcus Lindblom
A: 

There does not seem any point in implementing the constructor in another class.
All you are doing is breaking encapsulation and setting up your class for abuse.

The constructor is supposed to initialize the object into a good state as defined by the class. You are allowing another object to initialize your class. What guarantees do you have that this template class knows how to initialize your class correctly? A user of your class can provide any object that could mess with the internal state of your object in ways not intended.

The class should be self contained and initialize itself to a good state. What you seem to be doing is playing with templates just to see what they can do.

Martin York