协慌网

登录 贡献 社区

什么是 C ++ 仿函数及其用途?

我一直在听 C ++ 中的仿函数。有人可以给我一个关于它们是什么的概述以及在什么情况下它们会有用吗?

答案

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

// this is a functor
struct add_x {
  add_x(int x) : x(x) {}
  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() 。这意味着它可以内联该函数调用。这使得它就像我在向量的每个值上手动调用函数一样高效。

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

一点点补充。您可以使用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,你可以从类方法创建 functor,实际上这是一个委托:

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

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

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