Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are C++ functors and their uses?

I keep hearing a lot about functors in C++. Can someone give me an overview as to what they are and in what cases they would be useful?

like image 479
Konrad Avatar asked Dec 10 '08 17:12

Konrad


People also ask

What is a functor example?

Morphisms in this category are natural transformations between functors. Functors are often defined by universal properties; examples are the tensor product, the direct sum and direct product of groups or vector spaces, construction of free groups and modules, direct and inverse limits.

What are binary functors?

In mathematics, a binary function (also called bivariate function, or function of two variables) is a function that takes two inputs.

How do you pass a functor to a function in C++?

You cannot pass a functor as a function pointer into a function that takes a function pointer, even if the functor has the same arguments and return value as the function pointer. Similarly, if a function expects a functor, you cannot pass in a function pointer.

What are Functors Java?

In Java, the word method refers to the same kind of thing that the word function is used for in other languages. Specifically, a method is a function that belongs to a class. A function is a reusable portion of a program, sometimes called a procedure or subroutine.


2 Answers

A functor is pretty much just a class which defines the operator(). That lets you create objects which "look like" a function:

// this is a functor struct add_x {   add_x(int val) : x(val) {}  // Constructor   int operator()(int y) const { return x + y; }  private:   int x; };  // Now you can use it like this: add_x add42(42); // create an instance of the functor class int i = add42(8); // and "call" it assert(i == 50); // and it added 42 to its argument  std::vector<int> in; // assume this contains a bunch of values) std::vector<int> out(in.size()); // Pass a functor to std::transform, which calls the functor on every element  // in the input sequence, and stores the result to the output sequence std::transform(in.begin(), in.end(), out.begin(), add_x(1));  assert(out[i] == in[i] + 1); // for all i 

There are a couple of nice things about functors. One is that unlike regular functions, they can contain state. The above example creates a function which adds 42 to whatever you give it. But that value 42 is not hardcoded, it was specified as a constructor argument when we created our functor instance. I could create another adder, which added 27, just by calling the constructor with a different value. This makes them nicely customizable.

As the last lines show, you often pass functors as arguments to other functions such as std::transform or the other standard library algorithms. You could do the same with a regular function pointer except, as I said above, functors can be "customized" because they contain state, making them more flexible (If I wanted to use a function pointer, I'd have to write a function which added exactly 1 to its argument. The functor is general, and adds whatever you initialized it with), and they are also potentially more efficient. In the above example, the compiler knows exactly which function std::transform should call. It should call add_x::operator(). That means it can inline that function call. And that makes it just as efficient as if I had manually called the function on each value of the vector.

If I had passed a function pointer instead, the compiler couldn't immediately see which function it points to, so unless it performs some fairly complex global optimizations, it'd have to dereference the pointer at runtime, and then make the call.

like image 118
jalf Avatar answered Nov 18 '22 15:11

jalf


Little addition. You can use boost::function, to create functors from functions and methods, like this:

class Foo { public:     void operator () (int i) { printf("Foo %d", i); } }; void Bar(int i) { printf("Bar %d", i); } Foo foo; boost::function<void (int)> f(foo);//wrap functor f(1);//prints "Foo 1" boost::function<void (int)> b(&Bar);//wrap normal function b(1);//prints "Bar 1" 

and you can use boost::bind to add state to this functor

boost::function<void ()> f1 = boost::bind(foo, 2); f1();//no more argument, function argument stored in f1 //and this print "Foo 2" (: //and normal function boost::function<void ()> b1 = boost::bind(&Bar, 2); b1();// print "Bar 2" 

and most useful, with boost::bind and boost::function you can create functor from class method, actually this is a delegate:

class SomeClass {     std::string state_; public:     SomeClass(const char* s) : state_(s) {}      void method( std::string param )     {         std::cout << state_ << param << std::endl;     } }; SomeClass *inst = new SomeClass("Hi, i am "); boost::function< void (std::string) > callback; callback = boost::bind(&SomeClass::method, inst, _1);//create delegate //_1 is a placeholder it holds plase for parameter callback("useless");//prints "Hi, i am useless" 

You can create list or vector of functors

std::list< boost::function<void (EventArg e)> > events; //add some events .... //call them std::for_each(         events.begin(), events.end(),          boost::bind( boost::apply<void>(), _1, e)); 

There is one problem with all this stuff, compiler error messages is not human readable :)

like image 40
Evgeny Lazin Avatar answered Nov 18 '22 15:11

Evgeny Lazin