什么是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提供的有用。

相关文章
相关标签/搜索