15 Function Object
简单的理解,仿函数就是一个类,这个类定义了一个 operator() 函数,使用的时候调用的就是这个函数。
特点:
(1) 它可以具有函数状态(因为可以有成员变量,可以通过构造函数初始化)
(2) 每个函数对象都可以有自己的类型(因为可以定义仿函数类模板)
(3) 它的调用速度比普通函数更快(因为在编译阶段,编译器做了更多的优化,有更多的信息)
例1:
#include <list>
#include <algorithm>
#include <iostream>using namespace std;class AddValue {
private:int m_value;
public:AddValue(int value) :m_value(value) {}void operator() (int& elem) const { elem += m_value; }
};void main()
{list<int> coll;for (int i = 1; i <= 9; i++)coll.push_back(i);// 每个成员都加10for_each(coll.begin(), coll.end(), AddValue(10)); // 每个成员都加上第一个元素的值for_each(coll.begin(), coll.end(), AddValue(*coll.begin())); // 每个成员都加上变量X的值int x = 20;AddValue addx(x);for_each(coll.begin(), coll.end(), addx);
}
例2:设置排序准则
class Person {
public:string firstName;string lastName;
};class PersonSortCriterion {
public:bool operator() (const Person& p1, const Person& p2) {return p1.lastName < p2.lastName ||(p1.lastName == p2.lastName && p1.firstName < p2.firstName);}
};void main()
{set<Person, PersonSortCriterion> setPerson; // 指定set的排序准则for (auto pos = setPerson.begin(); pos != setPerson.end(); ++pos){// ....}
}
例3:使用仿函数状态
class IntSequence {
private:int value;
public:IntSequence(int initValue) : value(initValue){}int operator() () { return ++value; }
};void main()
{list<int> coll;IntSequence seq(1);// insert values from 1 to 4// - pass function object by reference// so that it will continue with 5generate_n<back_insert_iterator<list<int> >, int, IntSequence& > (back_inserter(coll), 4, seq);// insert values from 42 to 45generate_n(back_inserter(coll), 4, IntSequence(42));// continue with first sequence// - pass function object by value// so that it will continue with 5 againgenerate_n(back_inserter(coll), 4, seq);// continue with first sequence againgenerate_n(back_inserter(coll), 4, seq);
}
例4:for_each() 的返回值
for_each() 具有返回其函数对象的独特能力,可利用这点检查函数对象的最终状态
class MeanValue {
private:long num;long sum;
public:MeanValue():num(0),sum(0){}void operator() (int elem) {++num;sum += elem;}double value() {return static_cast<double>(sum);}
};void main()
{vector<int> coll = { 1,2,3,4,5,6,7,8 };MeanValue mv = for_each(coll.begin(), coll.end(), MeanValue());cout << "mean value " << mv.value() << endl;
}
例5:函数对象与判断式
class Nth {
private:int nth;int count;
public:Nth(int n) : nth(n), count(0) {}bool operator() (int) {return ++count == nth;}
};void main()
{list<int> coll = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };PRINT_ELEMENTS(coll, "coll: ");list<int>::iterator pos;pos = remove_if(coll.begin(), coll.end(), Nth(3));coll.erase(pos, coll.end());PRINT_ELEMENTS(coll, "3rd removed: ");
}
-------------------------------------------
coll: 1 2 3 4 5 6 7 8 9 10
3rd removed : 1 2 4 5 7 8 9 10
删除了第3个和第6个元素,这是因为 remove_if 的内部复制了op,传递的是一个副本,而不是引用。算法先用find_if找到一个需要删除的元素,删除后再循环调用find_if查找下一个需要删除的元素。这样的结果并不是我们想要的,因此,判断式应该是始终无状态的。谓词不应该因为调用而改变其状态,而且谓词的副本应该具有与原始谓词相同的状态。应该将 operator() 声明为常量成员函数。
预定义对象和绑定器
c++标准库提供了许多预定义的函数对象和绑定器,允许您将它们组合成更复杂的函数对象。这种能力称为函数组合,需要基本的函数对象和适配器
#include <functional>
1. 预定义函数对象列表
函数对象 |
作用 |
negate<type>() |
- param |
plus<type>() |
param1 + param2 |
minus<type>() |
param1 - param2 |
multiplies<type>() |
param1 * param2 |
divides<type>() |
param1 / param2 |
modulus<type>() |
param1 % param2 |
equal_to<type>() |
param1 == param2 |
not_equal_to<type>() |
param1 != param2 |
less<type>() |
param1 < param2 |
greater<type>() |
param1 > param2 |
less_equal<type>() |
param1 <= param2 |
greater_equal<type>() |
param1 >= param2 |
logical_not<type>() |
! param |
logical_and<type>() |
param1 && param2 |
logical_or<type>() |
param1 || param2 |
bit_and<type>() |
param1 & param2 |
bit_or<type>() |
param1 | param2 |
bit_xor<type>() |
param1 ^ param2 |
当对象被排序或通过排序函数和关联容器进行比较时,less<>是默认的条件。当对象被排序或通过排序函数和关联容器进行比较时,less<>是默认的条件。
2. 函数适配器和绑定器
函数适配器是一个函数对象,它允许函数对象彼此组合、使用特定值组合或使用特殊函数组合.
列出自c++ 11以来由c++标准库提供的函数适配器
函数适配器 |
作用 |
bind(op,args...) |
将args绑定到op |
mem_fn(op) |
作为对象的成员函数或对象指针调用op() |
not1(op) |
一元:否定 !op(param) |
not2(op) |
二元否定:!op (param1 param2) |
最重要的适配器是bind()。它允许:
- 根据现有的或预定义的函数对象调整和组合成新的函数对象
- 调用全局函数
- 调用函数对象、对象指针、对象智能指针的成员函数
(1) bind() 适配器
一般而言bind()用来将参数绑定于可调用对象。因此,如果一个函数、成员函数、函数对象或lambada需要若干参数,就可以将参数绑定为明白指出的或被传入的实参。欲明白指出,只需要写出其名称,欲使用被传入的实参,则可利用预定义的占位符 _1、_2、...,他们被定义于命名空间std::placeholders内。
binder的一个典型应用是,当使用标准库预定义函数对象时,具体制定参数。
#include <functional>
#include <iostream>
int main()
{auto plus10 = std::bind(std::plus<int>(),std::placeholders::_1,10);std::cout << "+10: " << plus10(7) << std::endl;auto plus10times2 = std::bind(std::multiplies<int>(),std::bind(std::plus<int>(),std::placeholders::_1,10),2);std::cout << "+10 *2: " << plus10times2(7) << std::endl;auto pow3 = std::bind(std::multiplies<int>(),std::bind(std::multiplies<int>(),std::placeholders::_1,std::placeholders::_1),std::placeholders::_1);std::cout << "x*x*x: " << pow3(7) << std::endl;auto inversDivide = std::bind(std::divides<double>(),std::placeholders::_2,std::placeholders::_1);std::cout << "invdiv: " << inversDivide(49, 7) << std::endl;
}
如果把binder传递给算法, 会作用于所操作的每一个元素身上
// add 10 to each element
std::transform(coll.begin(), coll.end(), // sourcecoll.begin(), // destinationstd::bind(std::plus<int>(), std::placeholders::_1, 10)// operation);
可以把binder作为一个排序准则
// find first element >42
auto pos = std::find_if(coll.begin(), coll.end(),std::bind(std::greater<int>(), _1, 42))
调用全局函数
#include <iostream>
#include <algorithm>
#include <functional>
#include <locale>
#include <string>
using namespace std;
using namespace std::placeholders;
char myToupper(char c)
{std::locale loc;return std::use_facet<std::ctype<char> >(loc).toupper(c);
}int main()
{string s("Internationalization");string sub("Nation");// search substring case insensitivestring::iterator pos;pos = search(s.begin(), s.end(), // string to search insub.begin(), sub.end(), // substring to searchbind(equal_to<char>(), // compar. criterionbind(myToupper, _1),bind(myToupper, _2)));// 相当于创建 myToupper(param1) == myToupper(param2)if (pos != s.end()) {cout << "\"" << sub << "\" is part of \"" << s << "\""<< endl;}
}
注意:bind()内部会复制被传入的实参。若要改变这种行为,可利用ref()或cref()。
void incr(int& i)
{++i;
}
int i = 0;
bind(incr, i)(); // increments a copy of i, no effect for i
bind(incr, ref(i))(); // increments i
调用成员函数
bind()可以调用对象的成员数、对象指针、对象智能指针的成员函数,virtual函数也能正确调用。
using namespace std;
using namespace std::placeholders;
class Person {
private:string name;
public:Person(const string& n) : name(n) {}void print() const { cout << name << endl; }void print2(const string& prefix) const { cout << prefix << name << endl;}
};int main()
{vector<Person> coll = { Person("Tick"), Person("Trick"), Person("Track") };// call member function print() for each personfor_each(coll.begin(), coll.end(), bind(&Person::print, _1));cout << endl;// call member function print2() with additional argument for each personfor_each(coll.begin(), coll.end(), bind(&Person::print2, _1, "Person: "));cout << endl;// call print2() for temporary Personbind(&Person::print2, _1, "This is: ")(Person("nico"));
}
(2) mem_fn() 适配器
对于成员函数,可以改用mem_fn(),那就不再需要以占位符表示调用者:
std::for_each(coll.begin(), coll.end(),std::mem_fn(&Person::print));
若有额外实参被传给成员函数,mem_fn()就拿其中第一个实参作为调用者,其他实参当做成员函数的实参:
std::mem_fn(&Person::print)(n); // calls n.print()
std::mem_fn(&Person::print2)(n, "Person: "); // calls n.print2("Person: ")
如果要为函数对象绑定额外的参数,还是要使用bind():
std::for_each(coll.begin(), coll.end(),std::bind(std::mem_fn(&Person::print2),std::placeholders::_1,"Person: "));
绑定至数据成员
map<string, int> coll; // map of int values associated to strings
// accumulate all values (member second of the elements)
int sum = accumulate(coll.begin(), coll.end(),0,bind(plus<int>(),_1,bind(&map<string, int>::value_type::second,_2)));
(3) not1()和not2()
这两个适配器几乎可被视为过时,他们的唯一作用就是令预定义的函数对象的意义相反。
(4) 函数适配器搭配自定义的函数对象
#include <iterator>
#include <functional>
#include "fopow.hpp"
using namespace std;
using namespace std::placeholders;
int main()
{vector<int> coll = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };// print 3 raised to the power of all elementstransform(coll.begin(), coll.end(), // sourceostream_iterator<float>(cout, " "), // destinationbind(fopow<float, int>(), 3, _1)); // operationcout << endl;// print all elements raised to the power of 3transform(coll.begin(), coll.end(), // sourceostream_iterator<float>(cout, " "), // destinationbind(fopow<float, int>(), _1, 3)); // operationcout << endl;
}
15 Function Object相关推荐
- The Apply method of function object
2019独角兽企业重金招聘Python工程师标准>>> http://webreference.com As explained in the previous page, Java ...
- C++Function Object Adapter之not1
相关博文:C++头文件<functional>和bind.placeholders占位符使用简单例子 相关博文:<Essential C++>笔记之设计一个泛型算法(二) 相关 ...
- 深入探究 Function Object 鸡蛋问题 侵立删
引言 上篇文章用图解的方式向大家介绍了原型链及其继承方案,在介绍原型链继承的过程中讲解原型链运作机制以及属性遮蔽等知识,今天这篇文章就来深入探究下 Function.__proto__ === Fun ...
- TypeError: 'function' object is not subscriptable
问题描述: 在进行网格化数据与实测数据的转换过程中,python调用numpy中的函数出现TypeError: 'function' object is not subscriptable的错误 解决 ...
- 【Django】‘function‘ object has no attribute ‘subjects‘报错的解决
项目场景 创建了一个新文件夹 myapp ,用于保存新功能模块下实现功能的代码文件 views.py . 问题描述 运行项目时,报错 'function' object has no attribut ...
- 认识js函数对象(Function Object)
可以用function关键字定义一个函数,对于每个函数可以为其指定一个函数名,通过函数名来进行调用.这些都是代码给用户的印象,而在JavaScript解释执行的时候,实际上每个函数都是被维护为一个 ...
- etree.html 报错 AttributeError:‘function’ object has no attribut ‘HTML’
etree.html 报错 1.Pycharm 中lxml没有etree模块的解决方法: 之前是: from lxml import etree tree = etree.HTML( ) python ...
- c语言 谓词,C++ 谓词(predicate) 与 仿函数 ( functor (function object))
#谓词与函数对象 谓词 predicate C++ 标准定义谓词如下: The Predicate parameter is used whenever an algorithm expects a ...
- 原型和原型链的理解(Function,Object特例深入理解)
原型我们可以分为显式原型和隐式原型. 显式原型: 每个函数都有一个prototype属性,即显式原型(属性),它默认指向一个object空对象(称为原型对象): 而原型对象中有一个属性construc ...
最新文章
- linux 股票指南针,linux 基础命令 1
- 前端资源构建-Grunt环境搭建
- 【数学和算法】初识卡尔曼滤波器(四)
- React中的this指向问题
- 微信高并发资金交易系统设计方案——百亿红包背后的技术支撑
- python有限元传热求解_用python实现简单的有限元方法(一)
- 怎么解决电脑USB接口不识别U盘
- 2021年计算机软考时间公布啦
- win10开机小键盘灯不亮
- 求矩阵伪逆的matlab方法,手把手教学
- LS1043A 查看 MAC、BMI、QMI寄存器, 查看网口发包丢包情况
- word2vec训练中文词向量
- Solidity实现拍卖竞价,代码解析,著名代码文档出处
- 湖南财务大数据比赛代码2018-12-20
- WEB自动化_窗口截图/截屏_get_screenshot_as_file()、screenshot()
- Spring5-IOC、AOP、事务管理
- linux 引导内存,initramfs
- 如何更改 Microsoft 帐户管理员名称
- 使用Python调用ChatGPT
- Windows通过注册表删除文件资源管理器中无用的快捷方式