我经常听到很多关于c++中的函子。谁能给我一个概述,他们是什么,在什么情况下他们会有用?


当前回答

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


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

void* method(void* something)

因此,如果不做一些额外的事情,你就不能(以一种简单明显的方式)在线程中运行你的类中的方法。

在c++中处理线程的一个很好的方法是创建你自己的Thread类。如果你想运行MyClass类的方法,我所做的是,把那些方法转换成Functor派生类。

同样,Thread类有这样的方法: 静态void* startThread(void*参数) 指向该方法的指针将用作调用pthread_create(..)的参数。startThread(..)在arg中应该接收到的是一个void*类型的引用,它指向任何Functor派生类的堆中的实例,在执行时将被强制转换回Functor*,然后调用它的run()方法。

其他回答

将函数作为函子实现的一个很大的优点是,它们可以在调用之间维护和重用状态。例如,许多动态规划算法,如用于计算字符串之间的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];
    }
};

小之外。你可以使用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));

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

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

函子通常用于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++函子还可以帮助为矩阵类提供Matlab喜欢的访问样式。这里有一个例子。

函子基本上就是一个定义操作符()的类。这让你可以创建“看起来像”函数的对象:

// 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。这使得它们可以很好地定制。

As the last lines show, you often pass functors as arguments to other functions such as std::transform or the other standard library algorithms. You could do the same with a regular function pointer except, as I said above, functors can be "customized" because they contain state, making them more flexible (If I wanted to use a function pointer, I'd have to write a function which added exactly 1 to its argument. The functor is general, and adds whatever you initialized it with), and they are also potentially more efficient. In the above example, the compiler knows exactly which function std::transform should call. It should call add_x::operator(). That means it can inline that function call. And that makes it just as efficient as if I had manually called the function on each value of the vector.

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