What are C++ functors and their uses?

后端 未结 14 1496
花落未央
花落未央 2020-11-21 04:27

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?

相关标签:
14条回答
  • 2020-11-21 04:56

    Functors are used in gtkmm to connect some GUI button to an actual C++ function or method.


    If you use the pthread library to make your app multithreaded, Functors can help you.
    To start a thread, one of the arguments of the pthread_create(..) is the function pointer to be executed on his own thread.
    But there's one inconvenience. This pointer can't be a pointer to a method, unless it's a static method, or unless you specify it's class, like class::method. And another thing, the interface of your method can only be:

    void* method(void* something)
    

    So you can't run (in a simple obvious way), methods from your class in a thread without doing something extra.

    A very good way of dealing with threads in C++, is creating your own Thread class. If you wanted to run methods from MyClass class, what I did was, transform those methods into Functor derived classes.

    Also, the Thread class has this method: static void* startThread(void* arg)
    A pointer to this method will be used as an argument to call pthread_create(..). And what startThread(..) should receive in arg is a void* casted reference to an instance in heap of any Functor derived class, which will be casted back to Functor* when executed, and then called it's run() method.

    0 讨论(0)
  • 2020-11-21 04:58

    A Functor is a object which acts like a function. Basically, a class which defines operator().

    class MyFunctor
    {
       public:
         int operator()(int x) { return x * 2;}
    }
    
    MyFunctor doubler;
    int x = doubler(5);
    

    The real advantage is that a functor can hold state.

    class Matcher
    {
       int target;
       public:
         Matcher(int m) : target(m) {}
         bool operator()(int x) { return x == target;}
    }
    
    Matcher Is5(5);
    
    if (Is5(n))    // same as if (n == 5)
    { ....}
    
    0 讨论(0)
  • 2020-11-21 04:59

    Except for used in callback, C++ functors can also help to provide a Matlab liking access style to a matrix class. There is a example.

    0 讨论(0)
  • 2020-11-21 04:59

    To add on, I have used function objects to fit an existing legacy method to the command pattern; (only place where the beauty of OO paradigm true OCP I felt ); Also adding here the related function adapter pattern.

    Suppose your method has the signature:

    int CTask::ThreeParameterTask(int par1, int par2, int par3)
    

    We will see how we can fit it for the Command pattern - for this, first, you have to write a member function adapter so that it can be called as a function object.

    Note - this is ugly, and may be you can use the Boost bind helpers etc., but if you can't or don't want to, this is one way.

    // a template class for converting a member function of the type int        function(int,int,int)
    //to be called as a function object
    template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
    class mem_fun3_t
    {
      public:
    explicit mem_fun3_t(_Ret (_Class::*_Pm)(_arg1,_arg2,_arg3))
        :m_Ptr(_Pm) //okay here we store the member function pointer for later use
        {}
    
    //this operator call comes from the bind method
    _Ret operator()(_Class *_P, _arg1 arg1, _arg2 arg2, _arg3 arg3) const
    {
        return ((_P->*m_Ptr)(arg1,arg2,arg3));
    }
    private:
    _Ret (_Class::*m_Ptr)(_arg1,_arg2,_arg3);// method pointer signature
    };
    

    Also, we need a helper method mem_fun3 for the above class to aid in calling.

    template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
    mem_fun3_t<_Ret,_Class,_arg1,_arg2,_arg3> mem_fun3 ( _Ret (_Class::*_Pm)          (_arg1,_arg2,_arg3) )
    {
      return (mem_fun3_t<_Ret,_Class,_arg1,_arg2,_arg3>(_Pm));
    }
    

    Now, in order to bind the parameters, we have to write a binder function. So, here it goes:

    template<typename _Func,typename _Ptr,typename _arg1,typename _arg2,typename _arg3>
    class binder3
    {
    public:
    //This is the constructor that does the binding part
    binder3(_Func fn,_Ptr ptr,_arg1 i,_arg2 j,_arg3 k)
        :m_ptr(ptr),m_fn(fn),m1(i),m2(j),m3(k){}
    
     //and this is the function object 
     void operator()() const
     {
            m_fn(m_ptr,m1,m2,m3);//that calls the operator
        }
    private:
        _Ptr m_ptr;
        _Func m_fn;
        _arg1 m1; _arg2 m2; _arg3 m3;
    };
    

    And, a helper function to use the binder3 class - bind3:

    //a helper function to call binder3
    template <typename _Func, typename _P1,typename _arg1,typename _arg2,typename _arg3>
    binder3<_Func, _P1, _arg1, _arg2, _arg3> bind3(_Func func, _P1 p1,_arg1 i,_arg2 j,_arg3 k)
    {
        return binder3<_Func, _P1, _arg1, _arg2, _arg3> (func, p1,i,j,k);
    }
    

    Now, we have to use this with the Command class; use the following typedef:

    typedef binder3<mem_fun3_t<int,T,int,int,int> ,T* ,int,int,int> F3;
    //and change the signature of the ctor
    //just to illustrate the usage with a method signature taking more than one parameter
    explicit Command(T* pObj,F3* p_method,long timeout,const char* key,
    long priority = PRIO_NORMAL ):
    m_objptr(pObj),m_timeout(timeout),m_key(key),m_value(priority),method1(0),method0(0),
    method(0)
    {
        method3 = p_method;
    }
    

    Here is how you call it:

    F3 f3 = PluginThreadPool::bind3( PluginThreadPool::mem_fun3( 
          &CTask::ThreeParameterTask), task1,2122,23 );
    

    Note: f3(); will call the method task1->ThreeParameterTask(21,22,23);.

    The full context of this pattern at the following link

    0 讨论(0)
  • 2020-11-21 05:01

    For the newbies like me among us: after a little research I figured out what the code jalf posted did.

    A functor is a class or struct object which can be "called" like a function. This is made possible by overloading the () operator. The () operator (not sure what its called) can take any number of arguments. Other operators only take two i.e. the + operator can only take two values (one on each side of the operator) and return whatever value you have overloaded it for. You can fit any number of arguments inside a () operator which is what gives it its flexibility.

    To create a functor first you create your class. Then you create a constructor to the class with a parameter of your choice of type and name. This is followed in the same statement by an initializer list (which uses a single colon operator, something I was also new to) which constructs the class member objects with the previously declared parameter to the constructor. Then the () operator is overloaded. Finally you declare the private objects of the class or struct you have created.

    My code (I found jalf's variable names confusing)

    class myFunctor
    { 
        public:
            /* myFunctor is the constructor. parameterVar is the parameter passed to
               the constructor. : is the initializer list operator. myObject is the
               private member object of the myFunctor class. parameterVar is passed
               to the () operator which takes it and adds it to myObject in the
               overloaded () operator function. */
            myFunctor (int parameterVar) : myObject( parameterVar ) {}
    
            /* the "operator" word is a keyword which indicates this function is an 
               overloaded operator function. The () following this just tells the
               compiler that () is the operator being overloaded. Following that is
               the parameter for the overloaded operator. This parameter is actually
               the argument "parameterVar" passed by the constructor we just wrote.
               The last part of this statement is the overloaded operators body
               which adds the parameter passed to the member object. */
            int operator() (int myArgument) { return myObject + myArgument; }
    
        private: 
            int myObject; //Our private member object.
    }; 
    

    If any of this is inaccurate or just plain wrong feel free to correct me!

    0 讨论(0)
  • 2020-11-21 05:02

    Here's an actual situation where I was forced to use a Functor to solve my problem:

    I have a set of functions (say, 20 of them), and they are all identical, except each calls a different specific function in 3 specific spots.

    This is incredible waste, and code duplication. Normally I would just pass in a function pointer, and just call that in the 3 spots. (So the code only needs to appear once, instead of twenty times.)

    But then I realized, in each case, the specific function required a completely different parameter profile! Sometimes 2 parameters, sometimes 5 parameters, etc.

    Another solution would be to have a base class, where the specific function is an overridden method in a derived class. But do I really want to build all of this INHERITANCE, just so I can pass a function pointer????

    SOLUTION: So what I did was, I made a wrapper class (a "Functor") which is able to call any of the functions I needed called. I set it up in advance (with its parameters, etc) and then I pass it in instead of a function pointer. Now the called code can trigger the Functor, without knowing what is happening on the inside. It can even call it multiple times (I needed it to call 3 times.)


    That's it -- a practical example where a Functor turned out to be the obvious and easy solution, which allowed me to reduce code duplication from 20 functions to 1.

    0 讨论(0)
提交回复
热议问题