什麼是C ++函子及其用途?

我不斷聽到不少有關C ++中的函子的信息。 有人能夠概述一下它們是什麼,在什麼狀況下會有用嗎? 編程


#1樓

對於咱們中間的像我這樣的新手:通過一番研究,我發現jalf發佈的代碼作了什麼。 編程語言

函子是能夠像函數同樣被「調用」的類或結構對象。 經過重載() operator能夠實現這一點。 () operator (不肯定其調用的名稱)能夠接受任意數量的參數。 其餘運算符僅取兩個,即+ operator只能取兩個值(運算符的每一側一個)並返回您爲其重載的任何值。 您能夠在() operator內放入任意數量的參數,這就是它的靈活性。 ide

首先要建立函子,請先建立類。 而後,使用您選擇的類型和名稱的參數爲類建立一個構造函數。 在同一條語句中緊隨其後的是一個初始化器列表(它使用單個冒號運算符,這對我來講也是很新的東西),該列表使用構造函數的先前聲明的參數構造類成員對象。 而後() operator被重載。 最後,您聲明已建立的類或結構的私有對象。 函數

個人代碼(我發現jalf的變量名使人困惑) ui

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.
};

若是其中任何一個不許確或僅是錯誤,請隨時糾正我! this


#2樓

做爲補充,我使用了函數對象使現有的舊方法適合命令模式; (只有我感受到的OO範式真正的OCP之美的地方); 還在此處添加相關的功能適配器模式。 spa

假設您的方法具備簽名: 指針

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

咱們將看到如何使它適合Command模式-爲此,首先,您必須編寫成員函數適配器,以即可以將其稱爲函數對象。 code

注意-這很醜陋,多是您可使用Boost綁定助手等,可是,若是您不肯意或不肯意,這是一種方法。 orm

// 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
};

另外,咱們須要上述類的輔助方法mem_fun3來幫助調用。

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));

}

如今,爲了綁定參數,咱們必須編寫一個綁定函數。 所以,它去了:

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;
};

而且,一個輔助函數可使用活頁夾3類-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);
}

如今,咱們必須在Command類中使用它。 使用如下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;
}

這是你的稱呼:

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

注意:f3(); 將調用方法task1-> ThreeParameterTask(21,22,23);。

如下連接中此模式的完整上下文


#3樓

函子是將函數應用於參數化(即模板化)類型的高階函數 。 它是地圖高階函數的通常化。 例如,咱們能夠爲std::vector定義一個仿函數,以下所示:

template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::vector<U> fmap(F f, const std::vector<T>& vec)
{
    std::vector<U> result;
    std::transform(vec.begin(), vec.end(), std::back_inserter(result), f);
    return result;
}

當給定函數F接受T並返回U時,此函數採用std::vector<T>並返回std::vector<U> 。 函子沒必要在容器類型上定義,它也能夠爲任何模板類型定義,包括std::shared_ptr

template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::shared_ptr<U> fmap(F f, const std::shared_ptr<T>& p)
{
    if (p == nullptr) return nullptr;
    else return std::shared_ptr<U>(new U(f(*p)));
}

這是一個將類型轉換爲double的簡單示例:

double to_double(int x)
{
    return x;
}

std::shared_ptr<int> i(new int(3));
std::shared_ptr<double> d = fmap(to_double, i);

std::vector<int> is = { 1, 2, 3 };
std::vector<double> ds = fmap(to_double, is);

函子應遵循兩個定律。 第一個是身份定律,該定律規定,若是給函子一個身份函數,則該函數應與對該類型應用身份函數相同,即fmap(identity, x)應與identity(x)相同。 :

struct identity_f
{
    template<class T>
    T operator()(T x) const
    {
        return x;
    }
};
identity_f identity = {};

std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<int> is1 = fmap(identity, is);
std::vector<int> is2 = identity(is);

下一個定律是組成定律,該定律規定,若是給函子兩個函數的組合,則應與對第一個函數應用函子而後對第二個函數再次應用函子相同。 所以, fmap(std::bind(f, std::bind(g, _1)), x)應該與fmap(f, fmap(g, x))

double to_double(int x)
{
    return x;
}

struct foo
{
    double x;
};

foo to_foo(double x)
{
    foo r;
    r.x = x;
    return r;
}

std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<foo> is1 = fmap(std::bind(to_foo, std::bind(to_double, _1)), is);
std::vector<foo> is2 = fmap(to_foo, fmap(to_double, is));

#4樓

在C ++出現以前好久,名稱「 functor」就一直在類別理論中使用。 這與functor的C ++概念無關。 最好使用名稱函數對象而不是咱們在C ++中稱爲「函數器」的函數。 這就是其餘編程語言如何調用相似的構造。

用於代替普通功能:

特徵:

  • 功能對象可能具備狀態
  • 函數對象適合OOP(它的行爲與其餘全部對象同樣)。

缺點:

  • 給程序帶來更多的複雜性。

用於代替函數指針:

特徵:

  • 函數對象一般能夠內聯

缺點:

  • 在運行時不能將函數對象與其餘函數對象類型交換(至少除非它擴展了一些基類,不然會產生一些開銷)

用於代替虛函數:

特徵:

  • 函數對象(非虛擬)不須要vtable和運行時調度,所以在大多數狀況下它更高效

缺點:

  • 在運行時不能將函數對象與其餘函數對象類型交換(至少除非它擴展了一些基類,不然會產生一些開銷)

#5樓

就像已經重複的那樣,函子是能夠視爲函數的類(重載運算符())。

在須要將某些數據與對函數的重複或延遲調用相關聯的狀況下,它們最有用。

例如,函子的鏈表可用於實現基本的低開銷同步協程系統,任務分派器或可中斷文件解析。 例子:

/* prints "this is a very simple and poorly used task queue" */
class Functor
{
public:
    std::string output;
    Functor(const std::string& out): output(out){}
    operator()() const
    {
        std::cout << output << " ";
    }
};

int main(int argc, char **argv)
{
    std::list<Functor> taskQueue;
    taskQueue.push_back(Functor("this"));
    taskQueue.push_back(Functor("is a"));
    taskQueue.push_back(Functor("very simple"));
    taskQueue.push_back(Functor("and poorly used"));
    taskQueue.push_back(Functor("task queue"));
    for(std::list<Functor>::iterator it = taskQueue.begin();
        it != taskQueue.end(); ++it)
    {
        *it();
    }
    return 0;
}

/* prints the value stored in "i", then asks you if you want to increment it */
int i;
bool should_increment;
int doSomeWork()
{
    std::cout << "i = " << i << std::endl;
    std::cout << "increment? (enter the number 1 to increment, 0 otherwise" << std::endl;
    std::cin >> should_increment;
    return 2;
}
void doSensitiveWork()
{
     ++i;
     should_increment = false;
}
class BaseCoroutine
{
public:
    BaseCoroutine(int stat): status(stat), waiting(false){}
    void operator()(){ status = perform(); }
    int getStatus() const { return status; }
protected:
    int status;
    bool waiting;
    virtual int perform() = 0;
    bool await_status(BaseCoroutine& other, int stat, int change)
    {
        if(!waiting)
        {
            waiting = true;
        }
        if(other.getStatus() == stat)
        {
            status = change;
            waiting = false;
        }
        return !waiting;
    }
}

class MyCoroutine1: public BaseCoroutine
{
public:
    MyCoroutine1(BaseCoroutine& other): BaseCoroutine(1), partner(other){}
protected:
    BaseCoroutine& partner;
    virtual int perform()
    {
        if(getStatus() == 1)
            return doSomeWork();
        if(getStatus() == 2)
        {
            if(await_status(partner, 1))
                return 1;
            else if(i == 100)
                return 0;
            else
                return 2;
        }
    }
};

class MyCoroutine2: public BaseCoroutine
{
public:
    MyCoroutine2(bool& work_signal): BaseCoroutine(1), ready(work_signal) {}
protected:
    bool& work_signal;
    virtual int perform()
    {
        if(i == 100)
            return 0;
        if(work_signal)
        {
            doSensitiveWork();
            return 2;
        }
        return 1;
    }
};

int main()
{
     std::list<BaseCoroutine* > coroutineList;
     MyCoroutine2 *incrementer = new MyCoroutine2(should_increment);
     MyCoroutine1 *printer = new MyCoroutine1(incrementer);

     while(coroutineList.size())
     {
         for(std::list<BaseCoroutine *>::iterator it = coroutineList.begin();
             it != coroutineList.end(); ++it)
         {
             *it();
             if(*it.getStatus() == 0)
             {
                 coroutineList.erase(it);
             }
         }
     }
     delete printer;
     delete incrementer;
     return 0;
}

固然,這些例子自己並非那麼有用。 它們僅顯示函子是如何有用的,函子自己是很是基本且不靈活的,這使它們不如Boost提供的有用。

相關文章
相關標籤/搜索