我不断听到很多有关C ++中的函子的信息。有人可以概述一下它们是什么,在什么情况下会有用吗?

评论

回答以下问题已涵盖此主题:stackoverflow.com/questions/317450/why-override-operator#317528

它用于在C ++中创建闭包。

查看下面的答案,如果有人想知道operator()(...)是什么意思:它正在重载“函数调用”运算符。对于()运算符,它只是运算符重载。不要在调用operator()函数时误认为operator()函数,而应将其视为通常的operator重载语法。

#1 楼

函子几乎只是一个定义operator()的类。这样,您就可以创建“看起来像”函数的对象:

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


关于函子,有很多不错的东西。一是与常规函数不同,它们可以包含状态。上面的示例创建了一个函数,该函数将42加到您提供的任何内容上。但是该值42不是硬编码的,在创建仿函数实例时将其指定为构造函数参数。我可以创建另一个加法器,只需用不同的值调用构造函数即可加27。

如最后几行所示,您经常将仿函数作为参数传递给其他函数,例如std :: transform或其他标准库算法。您可以对常规函数指针执行相同的操作,除了如上所述,函子可以“自定义”,因为函子包含状态,使它们更灵活(如果我想使用函子指针,则必须编写函子,它的参数正好加1。函子是通用的,并且加上您用其初始化的任何内容),它们也可能更有效。在上面的示例中,编译器确切知道std::transform应该调用哪个函数。它应该调用add_x::operator()。这意味着它可以内联该函数调用。就像我手动对向量的每个值调用函数一样,它的效率就很高。

如果我改为传递了一个函数指针,则编译器将无法立即看到它指向哪个函数因此,除非它执行一些相当复杂的全局优化,否则必须在运行时取消引用指针,然后进行调用。

评论


您能解释一下这一行吗,请std :: transform(in.begin(),in.end(),out.begin(),add_x(1));为什么在这里写add_x,而不是add42?

– Alecs
2011年9月12日14:48在

@Alecs两者都可以工作(但是效果会有所不同)。如果使用add42,则将使用之前创建的函子,并将42添加到每个值中。使用add_x(1)创建新的函子实例,该实例仅向每个值加1。只是表明,经常在需要时实例化函子,而不是先创建函子,然后在实际用于任何东西之前先保留函子。

–杰夫
2011-09-12 15:12



@zadane当然。他们只需要有operator(),因为调用者就是用来调用它的。函子还具有成员函数,构造函数,运算符和成员变量,这些完全由您决定。

–杰夫
2014年2月9日在12:37

@ rikimaru2013在函数式编程的说法中,您是对的,函数也是函子,但在C ++中,函子是专门用作函数的类。术语在早期曾被滥用,但这种区分是有用的区分,因此一直沿用至今。如果您开始在C ++上下文中将函数称为“函子”,那么您只会使对话感到困惑。

–srm
16-3-28在22:09



它是类还是类的实例?在大多数资源中,add42将被称为函子,而不是add_x(函子类或仅是函子类)。我发现该术语是一致的,因为函子也称为函数对象,而不是函数类。您能否澄清这一点?

–谢尔盖·塔切诺夫(Sergei Tachenov)
16年6月13日在14:41

#2 楼

一点点补充。您可以使用boost::function从函数和方法创建函子,如下所示:

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"


,并且可以使用boost :: bind向此函子添加状态

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"


最有用的是,使用boost :: bind和boost :: function可以从类方法创建函子,实际上这是一个委托:

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"


您可以创建函子的列表或向量

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


所有这些东西都有一个问题,编译器错误消息是不可读的:)

评论


在第一个示例中,由于类默认为private,所以operator()不应公开吗?

– NathanOliver
15年6月16日在13:43

也许在某个时候这个答案值得更新,因为现在lambdas是从任何东西中获得函子的最简单方法

–largest_prime_is_463035818
19年1月30日在18:29



int C ++ 11中有std :: function和std :: bind

–phuclv
10月25日9:26

#3 楼

函数是一个像函数一样起作用的对象。
基本上,是一个定义operator()的类。

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

MyFunctor doubler;
int x = doubler(5);


真正的优点是函子可以保持状态。

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)
{ ....}


评论


只需添加它们就可以像函数指针一样使用。

–马丁·约克
08年12月10日在18:04

@LokiAstari-对于那些不熟悉此概念的人,可能会产生误导。函子可以“像”一样使用,但并不总是“代替”函数指针。例如,使用函数指针的函数即使函数与函数指针具有相同的参数和返回值,也不能将函子代替。但是总体上,在设计时,函子是首选的且理论上“更现代”的方式。

–梅森·温莎(MasonWinsauer)
2014年3月12日21:49

为什么第二个在返回bool时返回int?这是C ++,不是C。编写此答案时,布尔不存在吗?

–莫妮卡基金的诉讼
16年5月8日在17:40



@QPaysTaxes我猜错字了。我可能从第一个示例复制并粘贴了代码,却忘了更改它。我已经修复了。

–詹姆斯·柯伦(James Curran)
16年5月10日在18:09

@Riasat如果Matcher在库中,则定义Is5()非常简单。您可以创建Is7(),Is32()等。此外,这只是一个示例。函子可能更加复杂。

–詹姆斯·柯伦(James Curran)
17年9月30日在2:57

#4 楼

在C ++出现之前很久,名称“ functor”就一直在类别理论中使用。这与functor的C ++概念无关。最好使用名称函数对象,而不是我们在C ++中称为“函数器”的函数。这就是其他编程语言如何调用类似的构造。

代替普通函数使用:

功能:



函数对象可能具有state
函数对象适合OOP(其行为与其他对象一样)。



使程序更加复杂。
/>

代替函数指针使用:

功能:


函数对象经常可以内联

/>缺点:


在运行时不能将函数对象与其他函数对象类型交换(至少除非它扩展了一些基类,因此会产生一些开销)


代替虚拟函数使用:

功能:


函数对象(非虚拟)不需要vtable和运行时调度,因此在大多数情况下它效率更高。

缺点:


在运行时不能将函数对象与其他函数对象类型交换(至少除非它扩展了)所以我的基类,因此会产生一些开销)


评论


您可以在实际示例中解释这些用例吗?我们如何使用函子作为多态性和函数指针?

– Milad Khajavi
13年2月21日在17:40

实际上,函子保持状态是什么意思?

– erogol
13年4月15日在12:05

感谢您指出一个基类需要某种多态性。我只是有一个问题,我必须在与简单函数指针相同的位置使用函子,而我发现的唯一方法是编写函子基类(因为我不能使用C ++ 11的东西)。不确定在我读完您的答案之前,这种开销是否有意义。

–largest_prime_is_463035818
2015年1月9日在12:36

@Erogol函数是一个碰巧支持语法foo(arguments)的对象。因此,它可以包含变量。例如,如果您有一个update_password(string)函数,则可能希望跟踪发生这种情况的频率;与函子一起使用,这可能是一段私人时间,代表上次发生的时间戳。使用函数指针或普通函数,您需要在其命名空间之外使用变量,该变量仅通过文档和用法而不是通过定义直接关联。

–莫妮卡基金的诉讼
16年5月9日在17:10

⁺¹提到名字是无缘无故的。我一直在寻找数学(或函数)函子和C ++中的函子之间的关系。

– Hi-Angel
17-10-15在18:25

#5 楼

就像其他人提到的一样,函子是一个像函数一样起作用的对象,即,它使函数调用运算符重载。

函子是STL算法中常用的函数。它们非常有用,因为它们可以在函数调用之前和之间保持状态,就像函数语言中的闭包一样。例如,您可以定义一个MultiplyBy函子,将其参数乘以指定数量:

class MultiplyBy {
private:
    int factor;

public:
    MultiplyBy(int x) : factor(x) {
    }

    int operator () (int other) const {
        return factor * other;
    }
};


然后可以将MultiplyBy对象传递给std :: transform之类的算法:

int array[5] = {1, 2, 3, 4, 5};
std::transform(array, array + 5, array, MultiplyBy(3));
// Now, array is {3, 6, 9, 12, 15}


函子相对于函数指针的另一个优点是,在更多情况下可以内联该调用。如果您将函数指针传递给transform,则除非该调用被内联并且编译器知道您始终将相同的函数传递给它,否则它将无法通过该指针内联该调用。

#6 楼

对于像我们这样的新手:经过一番研究之后,我弄清楚了jalf发布的代码是做什么的。

函子是一个类或结构对象,可以像函数一样被“调用”。通过使() operator过载可以实现这一点。 () operator(不确定其调用的名称)可以接受任意数量的参数。其他运算符仅取两个值,即+ operator只能取两个值(运算符的每一侧一个),并返回您为其重载的任何值。您可以在() operator中放入任意数量的参数,这使它具有更大的灵活性。

要创建函子,首先要创建类。然后,使用您选择的类型和名称的参数为类创建一个构造函数。在同一条语句中紧随其后的是一个初始化器列表(该列表使用单个冒号运算符,这对我来说还很陌生),该列表使用构造函数的先前声明的参数构造类成员对象。然后,() operator过载。最后,您声明您所创建的类或结构的私有对象。

我的代码(我发现jalf的变量名令人困惑)

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


如果其中任何一个不准确或仅是错误,请随时纠正我!

评论


()运算符称为函数调用运算符。我想您也可以将其称为括号运算符。

–高塔姆
13-10-12在20:19

“这个参数实际上是我们刚刚编写的构造函数传递的参数“ parameterVar”。

–轨道轻度竞赛
2015年9月8日在12:04

#7 楼

函子是将函数应用于参数化(即模板化)类型的高阶函数。它是地图高阶函数的一般化。例如,我们可以为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;
}


此函数采用std::vector<T>并在给定具有std::vector<U>的函数F并返回a T。函子不必在容器类型上定义,它也可以为任何模板类型定义,包括U

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


这里有一个简单的示例可以转换std::shared_ptr的类型:

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


函子应遵循两个定律。第一个是身份定律,该定律规定,如果给函子一个身份函数,则该函数应与对该类型应用身份函数相同,即double应该与fmap(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);


下一个定律是组成定律,该定律规定,如果给函子两个函数的组合,则应与将函子用于第一个函数,然后再对函子应用相同。第二功能。因此,identity(x)应该与fmap(std::bind(f, std::bind(g, _1)), 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));


评论


文章争论了functor应该正确地用于此含义(另请参见en.wikipedia.org/wiki/Functor),并且将其用于功能对象只是草率的:jackieokay.com/2017/01/26/functors.html鉴于此处给出的仅考虑功能对象含义的答案数量可能为时已晚。

– Armb
17年2月12日在13:33

这个答案应该是> 700 Upvotes的答案。有人比Haskell更了解Haskell,所以C ++语言一直困扰着我。

–mschmidt
17/12/26在21:34

类别理论和C ++?这是Bartosz Milewski的秘密SO帐户吗?

–玛蒂·乌尔哈克
18年8月7日在23:35

用标准符号总结函子定律可能会有所帮助:fmap(id,x)= id(x)和fmap(f◦g,x)= fmap(f,fmap(g,x))。

–玛蒂·乌尔哈克
18年8月7日在23:40



@mschmidt,而functor也表示这个意思,C ++重载名称以表示与“功能对象”相同的名称

– Caleth
19年7月2日在10:16

#8 楼

在一种实际情况下,我不得不使用Functor来解决我的问题:

我有一组函数(例如,其中有20个),它们都相同,除了每个函数调用不同的函数外在3个特定位置具有特定功能。

这是令人难以置信的浪费,并且代码重复。通常,我只是传递一个函数指针,然后在3个位置调用它。 (因此代码只需要出现一次,而不是出现二十次。)但是我意识到,在每种情况下,特定功能都需要一个完全不同的参数配置文件!有时2个参数,有时5个参数,等等。另一个解决方案是拥有一个基类,其中特定的函数是派生类中的重写方法。但是,我真的要构建所有这些继承关系,以便可以传递一个函数指针吗????

解决方案:所以我要做的是,制作了一个包装器类(“ Functor” ),它可以调用我需要调用的任何函数。我预先设置了它(带有其参数等),然后将其而不是函数指针传递给了它。现在,被调用的代码可以触发仿函数,而无需知道内部发生了什么。它甚至可以多次调用(我需要调用3次。)


就这样-一个实际的例子,其中一个Functor被证明是显而易见且简单的解决方案,允许我将代码重复从20个函数减少到1个。

评论


如果您的函子调用了不同的特定函数,而这些其他函数的接受参数数量有所不同,这是否意味着您的函子接受了可变数量的参数以分派给这些其他函数?

–约翰贝克
2014年2月28日在6:09

您能通过引用一些代码来解释上述情况吗,我是c ++的新手,想了解这个概念。

– sanjeev
17年5月5日上午10:13

#9 楼

在gtkmm中使用Functor将一些GUI按钮连接到实际的C ++函数或方法。


如果您使用pthread库使应用程序成为多线程,Functor可以为您提供帮助。 >要启动线程,pthread_create(..)的参数之一是要在自己的线程上执行的函数指针。
但有一个不便之处。除非它是静态方法,或者除非您指定它的类,例如class::method,否则该指针不能是方法的指针。另外,方法的接口只能是:

void* method(void* something)


所以您不能(以一种简单的明显方式)运行类中的方法线程而无需执行其他操作。

处理C ++中线程的一种很好的方法是创建自己的Thread类。如果要运行MyClass类中的方法,我要做的就是将这些方法转换为Functor派生类。

此外,Thread类也具有以下方法:
static void* startThread(void* arg)
该方法的指针将用作调用pthread_create(..)的参数。 startThread(..)应该在arg中收到的是对任何void*派生类的堆中的实例的Functor强制转换引用,该类在执行时将被强制转换回Functor*,然后称为run()方法。

评论


有一个例子吗?

– OS2
8月16日20:32

#10 楼

除了用于回调之外,C ++函子还可以帮助提供对矩阵类的Matlab喜欢的访问样式。有一个例子。

评论


这(矩阵示例)仅用于operator(),但未使用函数对象属性。

–狂欢
19年7月4日在9:40

#11 楼

就像重复的一样,函子是可以视为函数的类(重载运算符())。

它们在需要将某些数据与重复或延迟调用相关联的情况下最有用。函数。例如,可以使用函子的链接列表来实现基本的低开销同步协程系统,任务分派器或可中断文件解析。
示例:

/* 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提供的有用。

#12 楼

将函数实现为函子的一大优势在于,它们可以维护和重用调用之间的状态。例如,许多动态编程算法(例如用于计算字符串之间的Levenshtein距离的Wagner-Fischer算法)通过填写大型结果表来工作。每次调用该函数时分配该表的效率都很低,因此将函数实现为函子并使表成为成员变量可以极大地提高性能。

下面是实现Wagner- Fischer算法作为函子。请注意,表是如何在构造函数中分配的,然后在operator()中重新使用,并根据需要进行调整。

#include <string>
#include <vector>
#include <algorithm>

template <typename T>
T min3(const T& a, const T& b, const T& c)
{
   return std::min(std::min(a, b), c);
}

class levenshtein_distance 
{
    mutable std::vector<std::vector<unsigned int> > matrix_;

public:
    explicit levenshtein_distance(size_t initial_size = 8)
        : matrix_(initial_size, std::vector<unsigned int>(initial_size))
    {
    }

    unsigned int operator()(const std::string& s, const std::string& t) const
    {
        const size_t m = s.size();
        const size_t n = t.size();
        // The distance between a string and the empty string is the string's length
        if (m == 0) {
            return n;
        }
        if (n == 0) {
            return m;
        }
        // Size the matrix as necessary
        if (matrix_.size() < m + 1) {
            matrix_.resize(m + 1, matrix_[0]);
        }
        if (matrix_[0].size() < n + 1) {
            for (auto& mat : matrix_) {
                mat.resize(n + 1);
            }
        }
        // The top row and left column are prefixes that can be reached by
        // insertions and deletions alone
        unsigned int i, j;
        for (i = 1;  i <= m; ++i) {
            matrix_[i][0] = i;
        }
        for (j = 1; j <= n; ++j) {
            matrix_[0][j] = j;
        }
        // Fill in the rest of the matrix
        for (j = 1; j <= n; ++j) {
            for (i = 1; i <= m; ++i) {
                unsigned int substitution_cost = s[i - 1] == t[j - 1] ? 0 : 1;
                matrix_[i][j] =
                    min3(matrix_[i - 1][j] + 1,                 // Deletion
                    matrix_[i][j - 1] + 1,                      // Insertion
                    matrix_[i - 1][j - 1] + substitution_cost); // Substitution
            }
        }
        return matrix_[m][n];
    }
};


#13 楼

作为补充,我使用了函数对象使现有的旧方法适合命令模式。 (只有我感觉到的OO范式真正的OCP之美的地方);还在这里添加了相关的功能适配器模式。
假设您的方法具有签名:
int CTask::ThreeParameterTask(int par1, int par2, int par3)

我们将看到如何将其适合于Command模式-为此,首先,您必须编写一个成员函数适配器,以便可以将其称为函数对象。
注意-这很丑陋,可能您可以使用Boost绑定帮助器等,但是如果您不希望这样做, ,这是一种方法。
// 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 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);
此模式的完整上下文位于以下链接

#14 楼

Functor也可以用于模拟在函数内定义局部函数。请参阅问题和其他内容。

但是本地函子无法访问外部自动变量。 lambda(C ++ 11)函数是更好的解决方案。