美文网首页
函数对象

函数对象

作者: 龙翱天际 | 来源:发表于2016-05-12 23:02 被阅读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);

相关文章

  • js函数对象和回调

    函数对象和实例对象 函数对象:将函数作为对象使用时,简称函数对象实例对象:new函数产生的对象,简单对象 ()的左...

  • Promsie深入2

    预备知识 函数对象与实例对象 函数对象:将函数作为对象使用,简称为函数对象实例对象:new 函数产生对象,简称为对...

  • js高三精炼 —— 引用类型(下)

    Function 函数是对象,函数名是指针 函数是对象,函数名是指针 函数是对象,函数名是指针 函数是对象,函数名...

  • JavaScript深入理解 —— 原型、原型链和继承

    普通对象和函数对象 函数对象:使用函数声明、函数表达式、Function构造函数创建的对象 函数实际上是对象,每个...

  • 2018-05-18 原型链

    1:普通对象与函数对象 JavaScript分为普通对象和函数对象,通过new Function创建的对象都是函数...

  • javascript 原型

    函数也是一个对象,当真正开始执行函数,执行环境会为函数分配2个空间:函数对象变量空间、函数对象空间 函数对象变量空...

  • 第十六章 string类和标准模板库(5)函数对象

    (五)函数对象 函数对象,也叫作函数符functor。函数符是可以以函数的方式与()结合的任意对象,包括函数名,函...

  • Fluent Python手册(一)

    把函数视为对象:一等函数 把函数视为对象 python函数是对象。 高阶函数 函数式编程的特点之一是使用高阶函数。...

  • 函数和函数调用

    函数和函数调用: help(函数对象):查看函数帮助文档 abs:函数对象 abs():函数调用 abs():绝对...

  • 原型和原型链

    我们将对象分为函数对象和普通对象,函数对象的级别要要高于普通对象,可以通过函数对象创建普通对象,但是无法通过普通对...

网友评论

      本文标题:函数对象

      本文链接:https://www.haomeiwen.com/subject/ligerttx.html