views:

384

answers:

2

Hi, I was wondering if anyone can help me with functors. I dont really understand what functors are and how they work I have tried googling it but i still dont get it. how do functors work and how do they work with templates

+6  A: 

A functor is basically a "function object". It's a single function which you have wrapped in a class or struct, and which you can pass to other functions.

They work by creating your own class or struct which overloads the function call operator (called operator() ). Typically, you create an instance of it by simply constructing it in-place as an argument to your function which takes a functor.

Suppose you have the following:

std::vector<int> counts;

Now, you want to increment all the counts that are contained in that vector. You could loop through them manually to increment them, or you could use a functor. A suitable functor, in this case, would look like this:

struct IncrementFunctor
{
    int operator() (int i)
    {
        return i + 1;
    }
}

IncrementFunctor is now a functor which takes any integer and increments it. To apply it to counts, you can use the std::transform function, which takes a functor as an argument.

std::transform(
    counts.begin(),       // the start of the input range
    counts.end(),         // the end of the input range
    counts.begin(),       // the place where transform should place new values. 
                          // in this case, we put it right back into the original list.
    IncrementFunctor());  // an instance of your functor

The syntax IncrementFunctor() creates an instance of that functor which is then passed directly to std::transform. You could, of course, create an instance as a local variable and pass it on, but this is much more convenient.

Now, onto templates. The type of the functor in std::transform is a template argument. This is because std::transform does not know (or care!) of which type your functor is. All it cares about is that it has a fitting operator() defined, for which it can do something like

newValue = functor(oldValue);

The compiler is pretty smart about templates, and can often figure out on its own what the template arguments are. In this case, the compiler realizes automatically that you're passing in a parameter of type IncrementFunctor, which is defined as a template type in std::transform. It does the same for the list, too, so the compiler automatically recognizes that the actual call would look like this:

std::transform<std::vector<int>::iterator, // type of the input iterator
               std::vector<int>::iterator, // type of the output iterator
               IncrementFunctor>(          // type of your functor
    counts.begin(),       // the start of the input range
    counts.end(),         // the end of the input range
    counts.begin(),       // the place where transform should place new values. 
                          // in this case, we put it right back into the original list.
    IncrementFunctor());  // an instance of your functor

It saves you quite a bit of typing. ;)

arke
+5  A: 

A functor is something that can be invoked/called with the function call operator, syntactically by appending (), with optionally an argument list inside the parentheses.

That's all a template needs. The thing on which this is invoked, as far as a template is concerned, is anything that allows this syntax -- or in other words, either a free function or an instance of a class that overrides operator()(). (A "free" function is just one that isn't a member, that is, it's a function at global scope or a function at the scope of a previously included namespace.)

Outside of template metaprogramming, we usually don't say that a free function is a a functor, and reserve that name for an instance of a class that overrides operator()():

struct Foo {
 public:
   void operator()( int i ) { // do something }
   void operator()( int i, char x ) { // do something else }
}

In C++ templates are compiled, so as long as the syntax makes sense, the compiler will happily use a function or a functor:

template<typename T> class Bar {
  private int j ;
  public:
    Bar( int i ) : j(i) {}
    void doIt(T t) {
     t( j ) ;
  }
}

Foo f;
extern void resize( int i ) ; // in some header

Bar<Foo> bf( 5 ) ;
// a Bar that is templated on Foo
Bar< void (&)(int)   > br( 5 ) ; 
// a Bar that is templated on a function taking int and returning void

br.doit( &resize ) ; // call resize with br.j
bf.doit( f ) ; // call Foo::operator()(int) on Foo f with bf.j
tpdi
+1, a better answer because you actually show an example of a _templated_ functor.
Pavel Minaev
Johannes Schaub - litb
tpdi
Johannes Schaub - litb
In any case, other than this minor issue, i really like your explanation. Good work mate
Johannes Schaub - litb