函数对象

2016-05-12  本文已影响47人  龙翱天际

函数对象

如果一个类定义了调用运算符,则该类的对象称作函数对象(function object)。

含有状态的函数对象类

和其他类一样,函数对象类除了operator()之外,也可以包含其他成员。如:

#include <iostream>
#include "vector"
#include "string"

using namespace std;
class PrintString {
public:
    PrintString(ostream &o = cout, char c = ' '): os(o), sep(c){ }
    void operator() (const string &s) const { os << s << sep;}
private:
    ostream &os;
    char sep;
};

int main(int argc, const char * argv[]) {
    // insert code here...
    std::cout << "Hello, World!\n";
    PrintString printer;
    printer("test1:");
    cout << endl;
    vector<string> dd = {"ss", "ddd", "eeee"};
    for_each(dd.begin(), dd.end(), PrintString(cerr, ' '));
    ostream &os = cout;
    char sep = '\n';
    printer("test2:");
    cout << endl;
    for_each(dd.begin(), dd.end(), [&os, sep](const string &a){
        os << a << sep;
    });
    return 0;
}

lambda是函数对象

[](const string &a, const string &b) {
    return a.size() < b.size(); 
}

如上例中编写的lambda表达式,编译器会将该表达式翻译成一个为命名类的的未命名对象,如:

class ShorterString {
public:
    bool operator() (const string &a, const string &b) const {
        return a.size() < b.size();
    }
};

表示lambda及相应捕获行为的类

当一个lambda表达式通过引用捕获变量时,将由程序负责确保lambda执行时引用所引的对象确实存在。因此,编译器可以直接使用该引用而无须在lambda产生的类中将其存储为数据成员。

    size_t sz = 9;
    auto wc = find_if(dd.begin(), dd.end(), [&sz](const string &a){
        return a.size() >= sz;
    });

相反,通过值捕获的变量被拷贝到lambda中。因此,这种lambda产生的类必须为每个值捕获的变量建立对应的数据成员,同时创建构造函数,令其使用捕获的变量的值来初始化数据成员。
lambda表达式:

    size_t sz = 9;
    auto wc = find_if(dd.begin(), dd.end(), [&sz](const string &a){
        return a.size() >= sz;
    });

产生的类如下:

class SizeComp {
public:
    SizeComp(size_t n): sz(n){}
    bool operator()(const string &s) const {
        return s.size() >= sz;
    }
private:
    size_t sz;
};

标准库定义的函数对象

标准库定义了一组表示算术运算符、关系运算符和逻辑运算符的类,每个类分别定义了一个执行命名操作的调用运算符。

    plus<int> intAdd;
    int sum intAdd(1, 5); //等价于sum = 6

降序排序svec

sort(svec.begin(), svec.end(), greater<string>());

标准库定义的其他函数对象,详见:functional头文件

可调用对象与function

C++语言中有几种可调用的对象:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。
和其他对象一样,可调用的对象也有类型。例如,每个lambda有它自己唯一的(未命名)类类型;函数及函数指针的类型则由其返回值类型和实参类型决定,等等。
然而,两个不同类型的可调用对象却可能共享同一种调用形式(call signature)。调用形式指明了调用返回的类型以及传递给调用的实参类型。一种调用形式对应一个函数类型,例如:int(int, int)是一个函数类型,它接受两个int、返回一个int。

不同类型可能具有相同的调用形式

对于几个可调用对象共享同一种调用形式的情况,有时我们会希望把他们看成具有相同的类型。如:

int add(int i, int j) {return i +j;}
auto mod = [](int i, int j) {return i % j;};
struct divide {
    int operator()(int denominator, int divisor) {
        return denominator / divisor;
    }
};

上面这些可调用对象分别对其参数执行了不同的算术运算,尽管它们的类型各不相同,但是共享同一种调用形式:int(int, int)

标准库function类型

我们可能希望使用这些可调用对象构建一个简单的桌面计算器。为了实现这一目的,可以定义一个function对象表。

map<string, function<int(int, int)>> binops = {
    {"+", add},
    {"-", std::minus<int>()},
    {"/", divide()},
    {"*", [](int i, int j) {return i * j;}},
    {"%", mod}
};

调用方式如下:

    binops["+"](10, 5);
    binops["-"](10, 5);
    binops["/"](10, 5);
    binops["*"](10, 5);
    binops["%"](10, 5);
上一篇 下一篇

猜你喜欢

热点阅读