ChatGPT解决这个技术问题 Extra ChatGPT

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

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

此主题已针对此问题进行了介绍:stackoverflow.com/questions/317450/why-override-operator#317528
它用于在 C++ 中创建闭包。
看看下面的答案,如果有人想知道 operator()(...) 是什么意思:它重载了 "function call" 运算符。它只是 () 运算符的运算符重载。不要把 operator() 误认为是调用一个名为 operator 的函数,而是将其视为通常的运算符重载语法。

r
radical7

函子几乎只是一个定义 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 两者都会起作用(但效果会有所不同)。如果我使用 add42,我会使用我之前创建的函子,并将 42 添加到每个值。使用 add_x(1),我创建了一个新的仿函数实例,它只为每个值加 1。这只是为了表明,您经常在需要时“即时”实例化函子,而不是先创建它,然后在实际使用它之前保留它。
@zadane 当然。他们只需要有 operator(),因为这是调用者用来调用它的。 else 函子对成员函数、构造函数、运算符和成员变量的作用完全取决于您。
@rikimaru2013 在函数式编程的说法中,你是对的,函数也是函子,但在 C++ 的说法中,函子是专门用作函数的类。这个术语在早期有点被滥用,但这个划分是有用的区分,所以今天仍然存在。如果您开始在 C++ 上下文中将函数称为“函子”,那么您只会混淆对话。
它是类还是类的实例?在大多数来源中,add42 将被称为函子,而不是 add_x(它是函子的类或只是函子类)。我发现这个术语是一致的,因为函子也称为函数对象,而不是函数类。你能澄清这一点吗?
Z
Ziezi

一点补充。您可以使用 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));

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


由于类默认为私有,在您的第一个示例中不应该 operator () 是公开的吗?
也许在某些时候这个答案值得更新,因为现在 lambdas 是从任何东西中获取函子的最简单方法
int C++11 有 std::functionstd::bind
Z
Ziezi

Functor 是一个像函数一样工作的对象。基本上,一个定义 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)
{ ....}

只需要补充一点,它们可以像函数指针一样使用。
@LokiAstari - 对于那些不熟悉这个概念的人来说,这可能有点误导。函子可以“像”一样使用,但并不总是“代替”函数指针。例如,一个接受函数指针的函数不能在其位置上取一个函子,即使函子具有与函数指针相同的参数和返回值。但总的来说,在设计时,函子是首选且理论上“更现代”的方式。
为什么第二个应该返回 bool 时返回 int?这是 C++,而不是 C。写这个答案时,bool 不存在吗?
@QPaysTaxes 我猜是错字。我可能复制了第一个示例中的代码并忘记更改它。我现在已经修好了。
@Riasat 如果 Matcher 在库中,定义 Is5() 非常简单。并且您可以创建 Is7()、Is32() 等。此外,这只是一个示例。函子可能要复杂得多。
m
mip

早在 C++ 出现之前,category theory 中就一直使用名称“函子”。这与函子的 C++ 概念无关。最好使用名称 function object 而不是我们在 C++ 中所说的“functor”。这就是其他编程语言调用类似结构的方式。

用于代替普通功能:

特征:

函数对象可能有状态

函数对象适合 OOP(它的行为与其他所有对象一样)。

缺点:

给程序带来了更多的复杂性。

用于代替函数指针:

特征:

函数对象通常可以内联

缺点:

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

代替虚函数使用:

特征:

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

缺点:

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


你能在实际例子中解释这些用例吗?我们如何将仿函数用作多态和函数指针?
函子持有状态究竟意味着什么?
感谢您指出需要一个基类才能具有某种多态性。我只是有一个问题,我必须在与简单函数指针相同的位置使用仿函数,而我发现的唯一方法是编写仿函数基类(因为我不能使用 C++11 的东西)。在我阅读您的答案之前,不确定这种开销是否有意义。
@Erogol 仿函数是一个恰好支持语法 foo(arguments) 的对象。因此,它可以包含变量;例如,如果您有一个 update_password(string) 函数,您可能想要跟踪它发生的频率;使用仿函数,它可以是一个 private long time,表示它最后发生的时间戳。使用函数指针或普通函数,您需要使用其命名空间之外的变量,该变量仅与文档和用法直接相关,而不是定义.l
⁺¹ 提到这个名字是无缘无故编造的。我一直在寻找数学(或函数,如果你想)函子和来自 C++ 的函子之间的关系。
L
Lightness Races in Orbit

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

函子通常用于 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,除非 那个 调用被内联并且编译器知道您总是将相同的函数传递给它,否则它不能通过指针内联调用。


我正在寻找这个示例,因为我刚刚在 C++ 课程中看到它并且我不理解它。通常,我们定义一个类的对象,它实现了 operator(),并将它作为一个参数,一个仿函数传递给一个函数,例如 transform。但是,在这种情况下,我们只是在同一个调用中构造对象。这是唯一的区别吗?仿函数超出范围并在 transform 完成后被销毁?谢谢!
Z
Ziezi

对于我们中间像我这样的新手:经过一番研究,我弄清楚了 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.
}; 

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


() 运算符称为函数调用运算符。我想您也可以将其称为括号运算符。
“这个参数其实就是我们刚才写的构造函数传递的参数”parameterVar“啊?
P
Paul Fultz II

仿函数是将函数应用于参数化(即模板化)类型的 higher-order function。它是 map 高阶函数的推广。例如,我们可以为 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 时,该函数接受一个 std::vector<T> 并返回 std::vector<U>,该函数接受一个 T 并返回一个 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));

文章认为函子应正确用于此含义(另请参见 en.wikipedia.org/wiki/Functor),并且将其用于函数对象只是草率:jackieokay.com/2017/01/26/functors.html 但考虑到这里的答案数量只有考虑函数对象的含义。
这个答案应该是 >700 Upvotes 的答案。作为一个比 C++ 更了解 Haskell 的人,C++ 语言一直让我感到困惑。
范畴论和C++?这是 Bartosz Milewski 的秘密 SO 帐户吗?
用标准表示法总结函子定律可能会有所帮助:fmap(id, x) = id(x)fmap(f ◦ g, x) = fmap(f, fmap(g, x))
C++ 标准中没有提到函子。 cppreference.com 没有提供函子的定义,但它确实提供了 FunctionObject 的定义,但根本没有提及函子。
F
Fellow Traveler

这是我被迫使用 Functor 解决问题的实际情况:

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

这是令人难以置信的浪费和代码重复。通常我只会传入一个函数指针,然后在 3 个位置调用它。 (所以代码只需要出现一次,而不是二十次。)

但后来我意识到,在每种情况下,特定功能都需要完全不同的参数配置文件!有时 2 个参数,有时 5 个参数,等等。

另一种解决方案是拥有一个基类,其中特定函数是派生类中的重写方法。但是我真的想构建所有这些继承,只是为了我可以传递一个函数指针????

解决方案:所以我所做的是,我创建了一个包装类(“Functor”),它能够调用我需要调用的任何函数。我提前设置了它(使用它的参数等),然后我将它而不是函数指针传递给它。现在被调用的代码可以触发 Functor,而不知道里面发生了什么。它甚至可以多次调用它(我需要它调用 3 次。)

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


如果您的仿函数调用了不同的特定函数,并且这些其他函数接受的参数数量不同,这是否意味着您的仿函数接受了可变数量的参数来分派给这些其他函数?
您能否通过引用部分代码来解释上述情况,我是 C++ 新手,想了解这个概念。
G
Guido Tarsia

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

如果您使用 pthread 库使您的应用程序多线程化,Functors 可以帮助您。
要启动一个线程,pthread_create(..) 的参数之一是要在他自己的线程上执行的函数指针。
但是有一个不便之处。此指针不能是指向方法的指针,除非它是静态方法,或者除非您指定它的类,例如 class::method。还有一件事,你方法的接口只能是:

void* method(void* something)

所以你不能(以一种简单明显的方式)在一个线程中运行你的类中的方法而不做额外的事情。

在 C++ 中处理线程的一种非常好的方法是创建自己的 Thread 类。如果您想从 MyClass 类运行方法,我所做的是将这些方法转换为 Functor 派生类。

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


有例子吗?
Y
Yantao Xie

除了用于回调之外,C++ 仿函数还可以帮助为 matrix 类提供一种 Matlab 喜欢的访问方式。有一个example


这(矩阵示例)是对 operator() 的简单使用,但没有使用函数对象属性。
n
nfries88

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

它们对于您需要将某些数据与对函数的重复或延迟调用相关联的情况最有用。

例如,函子的链表可用于实现基本的低开销同步协程系统、任务分派器或可中断的文件解析。例子:

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


M
Martin Broadhurst

将函数实现为函子的一大优势是它们可以在调用之间维护和重用状态。例如,许多动态编程算法(如用于计算字符串之间的 Levenshtein distanceWagner-Fischer algorithm)通过填写一个大的结果表来工作。每次调用函数时都分配这张表是非常低效的,所以将函数实现为仿函数并将表设为成员变量可以大大提高性能。

下面是一个将 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];
    }
};

p
phuclv

另外,我使用函数对象将现有的遗留方法适应命令模式; (唯一让我感受到OO范式真正OCP之美的地方);还在这里添加了相关的功能适配器模式。

假设您的方法具有签名:

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

我们将看到如何使它适合命令模式——为此,首先,您必须编写一个成员函数适配器,以便可以将其作为函数对象调用。

注意 - 这很丑,可能你可以使用 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;
};

并且,使用 binder3 类的辅助函数 - 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);

此模式的完整上下文位于以下 link


C
Community

Functor 还可用于模拟在函数中定义局部函数。请参阅 questionanother

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