函子


在调用函数时的()是调用运算符(call operator),你可以重载调用运算符。例如:

#include <iostream> 
using namespace std;

struct IntPlus {
    int operator()(int rhs, int lhs) const {
        return rhs + lhs;
    }
};

int main() { 
    IntPlus plus;
    cout << plus(10, 20) << endl;
    return 0; 
}

在范例中,p称为函数对象(function object),又称为函子(functor),是Callable类型,可以像函数一样地调用,范例中的plus可以指定给function<int(int, int)>类型的变量。

这边的IntPlus实例,相当于 lambda 表达式,[] (int rhs, int lhs) { return rhs + lhs; },lambda 表达式多半编译为匿名的函子,如果一个 lamdbda 表达式有捕捉变量呢?例如[a] (int b) { return a + b; },那么相当于底下的函子:

#include <iostream> 
using namespace std;

int main() { 
    class __anonymous {
        int a;
    public:
        __anonymous(int a) : a(a) {}
        int operator()(int b) const {
            return a + b;
        }
    };

    int a = 10;
    __anonymous f(a);

    cout << f(20) << endl;

    return 0; 
}

如果一个 lamdbda 表达式以参考方式捕捉变量呢?例如[&a] { a = 30; },那么相当于底下的函子:

#include <iostream> 
using namespace std;

int main() { 
    class __anonymous {
        int &a;
    public:
        __anonymous(int &a) : a(a) {}
        void operator()() const {
            a = 30;
        }
    };

    int a = 10;
    __anonymous f(a);

    f();        
    cout << a << endl; // 30

    return 0; 
}

既然如此,不如就使用 lamdbda 表达式就好了,还需要函子吗?一种说法因为编译器会对其最佳化,函子比较有效率,不过就目的来说,因为函子是个对象,它就可以携带更多的信息,例如:

#include <iostream> 
using namespace std;

class PrintLine {
    string sep;
public:
    PrintLine(string sep) : sep(sep) {}
    void operator()(string text) const {
        cout << text << sep;
    }
};

int main() { 
    PrintLine printLf("\n");
    PrintLine printCrLf("\r\n");

    printLf("print lf");
    printCrLf("print crlf");

    return 0; 
}

还有一个好处是函子可以模版化,在〈高阶函数〉中看过,functional中包含了对应于运算符的函子(Functor),像是plusminusmultiplies等,这些函子都模版化了,其中的范例就看过,创建函数对象时就可以指定类型:

#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;

int main() {
    auto add10 = bind(plus<int>{}, _1, 10);
    auto mul5 = bind(multiplies<int>{}, _1, 5); 

    cout << add10(30) << endl;  // 40
    cout << mul5(20) << endl;   // 100

    return 0;
}




展开阅读全文