【Boost】boost库中function的用法
要开始使用 Boost.Function, 就要包含头文件 "boost/function.hpp", 或者某个带数字的版本,从"boost/function/function0.hpp" 到 "boost/function/function10.hpp". 如果你知道你想保存在 function 中的函数的参数数量,这样做可以让编译器仅包含需要的头文件。如果包含 "boost/function.hpp", 那么就会把其它的头文件也包含进去。
理解被存函数的最佳方法是把它想象为一个普通的函数对象,该函数对象用于封装另一个函数(或函数对象)。这个被存的函数的最大用途是它可以被多次调用,而无须在创建 function 时立即使用。在声明 functions 时,声明中最重要的部分是函数的签名。这部分即是告诉 function 它将保存的函数或函数对象的签名和返回类型。我们已经看到,有两种方法来执行这个声明。这里有一个完整的程序,程序声明了一个 boost::function ,它可以保存返回bool (或某个可以隐式转换为 bool 的类型)并接受两个参数的类函数实体,第一个参数可以转换为 int, 第二个参数可以转换为 double.
- #include <iostream>
- #include "boost/function.hpp"
- bool some_func(int i,double d) {
- return i>d;
- }
- int main() {
- boost::function<bool (int,double)> f;
- f=&some_func;
- f(10,1.1);
- }
当 function f 首次创建时,它不保存任何函数。它是空的,可以在一个布尔上下文中进行测试。如果你试图调用一个没有保存任何函数或函数对象的 function ,它将抛出一个类型 bad_function_call 的异常。为了避免这个问题,我们用普通的赋值语法把一个指向 some_func 的指针赋值给 f 。这导致 f 保存了到 some_func 的指针。最后,我们用参数10 (一个 int) 和 1.1 (一个 double)来调用 f (用函数调用操作符)。要调用一个 function, 你必须提供被存函数或函数对象所期望的准确数量的参数。
回调的基础
我们先来看看在没有 Boost.Function 以前我们如何实现一个简单的回调,然后再把代码改为使用 function, 并看看会带来什么优势。我们从一个支持某种简单的回调形式的类开始,它可以向任何对新值关注的对象报告值的改变。这里的回调是一种传统的C风格回调,即使用普通函数。这种回调用可用于象GUI控制这样的场合,它可以通知观察者用户改变了它的值,而不需要对监听该信息的客户有任何特殊的知识。
- #include <iostream>
- #include <vector>
- #include <algorithm>
- #include "boost/function.hpp"
- void print_new_value(int i) {
- std::cout <<
- "The value has been updated and is now " << i << '/n';
- }
- void interested_in_the_change(int i) {
- std::cout << "Ah, the value has changed./n";
- }
- class notifier {
- typedef void (*function_type)(int);
- std::vector<function_type> vec_;
- int value_;
- public:
- void add_observer(function_type t) {
- vec_.push_back(t);
- }
- void change_value(int i) {
- value_=i;
- for (std::size_t i=0;i<vec_.size();++i) {
- (*vec_[i])(value_);
- }
- }
- };
- int main() {
- notifier n;
- n.add_observer(&print_new_value);
- n.add_observer(&interested_in_the_change);
- n.change_value(42);
- }
这里的两个函数,print_new_value 和 interested_in_the_change, 它们的函数签名都兼容于 notifier 类的要求。这些函数指针被保存在一个 vector 内,并且无论何时它的值被改变,这些函数都会在一个循环里被调用。调用这些函数的一种语法是:
(*vec_[i])(value_);
值(value_)被传递给解引用的函数指针(即 vec_[i] 所返回的)。另一种写法也是有效的,即这样:
vec_[i](value_);
这种写法看起来更好看些,但更为重要的是,它还可以允许你把函数指针更换为 Boost.Function 而没有改变调用的语法。现在,工作还是正常的,但是,唉,函数对象不能用于这个 notifier 类。事实上,除了函数指针以外,别的任何东西都不能用,这的确是一种局限。但是,如果我们使用 Boost.Function,它就可以工作。重写这个 notifier类非常容易。
- class notifier {
- typedef boost::function<void(int)> function_type;
- std::vector<function_type> vec_;
- int value_;
- public:
- template <typename T> void add_observer(T t) {
- vec_.push_back(function_type(t));
- }
- void change_value(int i) {
- value_=i;
- for (std::size_t i=0;i<vec_.size();++i) {
- vec_[i](value_);
- }
- }
- };
首先要做的事是,把 typedef 改为代表 boost::function 而不是函数指针。之前,我们定义的是一个函数指针;现在,我们使用泛型方法,很快就会看到它的用途。接着,我们把成员函数 add_observer 的签名改为泛化的参数类型。我们也可以把它改为接受一个 boost::function,但那样会要求该类的用户必须也知道 function 的使用方法[2],而不是仅仅知道这个观察者类型的要求就行了。应该注意到 add_observer 的这种变化并不应该是转向function 的结果;无论如何代码应该可以继续工作。我们把它改为泛型的;现在,不管是函数指针、函数对象,还是 boost::function 实例都可以被传递给 add_observer, 而无须对已有用户代码进行任何改动。把元素加入到vector 的代码有一些修改,现在需要创建一个 boost::function<void(int)> 实例。最后,我们把调用这些函数的语法改为可以使用函数、函数对象以及 boost::function 实例[3]。这种对不同类型的类似函数的"东西"的扩展支持可以立即用于带状态的函数对象,它们可以实现一些用函数很难做到的事情。
[2] 他们应该知道 Boost.Function,但如果他们不知道呢?我们添加到接口上的任何东西都必须及时向用户解释清楚。
[3] 现在我们知道,一开始我们就应该用这种语法。
- class knows_the_previous_value {
- int last_value_;
- public:
- void operator()(int i) {
- static bool first_time=true;
- if (first_time) {
- last_value_=i;
- std::cout <<
- "This is the first change of value, /
- so I don't know the previous one./n";
- first_time=false;
- return;
- }
- std::cout << "Previous value was " << last_value_ << '/n';
- last_value_=i;
- }
- };
这个函数对象保存以前的值,并在值被改变时把旧值输出到 std::cout 。注意,当它第一次被调用时,它并不知道旧值。这个函数对象在函数中使用一个静态 bool 变量来检查这一点,该变量被初始化为 true. 由于函数中的静态变量是在函数第一次被调用时进行初始化的,所以它仅在第一次调用时被设为 true 。虽然也可以在普通函数中使用静态变量来提供状态,但是我们必须知道那样不太好,而且很难做到多线程安全。因此,带状态的函数对象总是优于带静态变量的普通函数。notifier 类并不关心这是不是函数对象,只要符合要求就可以接受。以下更新的例子示范了它如何使用。
- int main() {
- notifier n;
- n.add_observer(&print_new_value);
- n.add_observer(&interested_in_the_change);
- n.add_observer(knows_the_previous_value());
- n.change_value(42);
- std::cout << '/n';
- n.change_value(30);
- }
关键一点要注意的是,我们新增的一个观察者不是函数指针,而是一个 knows_the_previous_value 函数对象的实例。运行这段程序的输出如下:
The value has been updated and is now 42 Ah, the value has changed. This is the first change of value, so I don't know the previous one.The value has been updated and is now 30 Ah, the value has changed. Previous value was 42
在这里最大的优点不是放宽了对函数的要求(或者说,增加了对函数对象的支持),而是我们可以使用带状态的对象,这是非常需要的。我们对 notifier 类所做的修改非常简单,而且用户代码不受影响。如上所示,把 Boost.Function 引入一个已有的设计中是非常容易的。
类成员函数
Boost.Function 不支持参数绑定,这在每次调用一个 function 就要调用同一个类实例的成员函数时是需要的。幸运的是,如果这个类实例被传递给 function 的话,我们就可以直接调用它的成员函数。这个 function 的签名必须包含类的类型以及成员函数的签名。换言之,显式传入的类实例要作为隐式的第一个参数,this。这样就得到了一个在给出的对象上调用成员函数的函数对象。看一下以下这个类:
- class some_class {
- public:
- void do_stuff(int i) const {
- std::cout << "OK. Stuff is done. " << i << '/n';
- }
- };
成员函数 do_stuff 要从一个 boost::function 实例里被调用。要做到这一点,我们需要 function 接受一个some_class 实例,签名的其它部分为一个 void 返回以及一个 int 参数。对于如何把 some_class 实例传给 function,我们有三种选择:传值,传引用,或者传址。如何要传值,代码就应该这样写[4]
[4] 很少会有理由来以传值的方式传递对象参数。
- boost::function<void(some_class,int)> f;
注意,返回类型仍旧在最开始,后跟成员函数所在的类,最后是成员函数的参数类型。它就象传递一个 this 给一个函数,该函数暗地里用类实例调用一个非成员函数。要把函数 f 配置为成员函数 do_stuff, 然后调用它,我们这样写:
- f=&some_class::do_stuff;
- f(some_class(),2);
如果要传引用,我们要改一下函数的签名,并传递一个 some_class 实例。
- boost::function<void(some_class&,int)> f;
- f=&some_class::do_stuff;
- some_class s;
- f(s,1);
最后,如果要传 some_class 的指针[5],我们就要这样写:
[5] 裸指针或智能指针皆可。
[cpp] view plaincopy
- boost::function<void(some_class*,int)> f;
- f=&some_class::do_stuff;
- some_class s;
- f(&s,3);
好了,所有这些传递"虚拟 this"实例的方法都已经在库中提供。当然,这种技术也是有限制的:你必须显式地传递类实例;而理想上,你更愿意这个实例被绑定在函数中。乍一看,这似乎是 Boost.Function 的缺点,但有别的库可以支持参数的绑定,如 Boost.Bind 和 Boost.Lambda. 我们将在本章稍后的地方示范这些库会给 Boost.Function 带有什么好处。
带状态的函数对象
我们已经看到,由于支持了函数对象,就可以给回调函数增加状态。考虑这样一个类,keeping_state, 它是一个带状态的函数对象。keeping_state 的实例记录一个总和,它在每次调用操作符执行时被增加。现在,将该类的一个实例用于两个 boost::function 实例,结果有些出人意外。
- #include <iostream>
- #include "boost/function.hpp"
- class keeping_state {
- int total_;
- public:
- keeping_state():total_(0) {}
- int operator()(int i) {
- total_+=i;
- return total_;
- }
- int total() const {
- return total_;
- }
- };
- int main() {
- keeping_state ks;
- boost::function<int(int)> f1;
- f1=ks;
- boost::function<int(int)> f2;
- f2=ks;
- std::cout << "The current total is " << f1(10) << '/n';
- std::cout << "The current total is " << f2(10) << '/n';
- std::cout << "After adding 10 two times, the total is "
- << ks.total() << '/n';
- }
写完这段代码并接着执行它,程序员可能期望保存在 ks 的总和是20,但不是;事实上,总和为0。以下是这段程序的运行结果。
The current total is 10 The current total is 10 After adding 10 two times, the total is 0
原因是每一个 function 实例(f1 和 f2)都含有一个 ks 的拷贝,这两个实例得到的总和都是10,但 ks 没有变化。这可能是也可能不是你想要的,但是记住,boost::function 的缺省行为是复制它要调用的函数对象,这一点很重要。如果这导致不正确的语义,或者如果某些函数对象的复制代价太高,你就必须把函数对象包装在boost::reference_wrapper 中,那样 boost::function 的复制就会是一个 boost::reference_wrapper 的拷贝,它恰好持有一个到原始函数对象的引用。你无须直接使用 boost::reference_wrapper ,你可以使用另两个助手函数,ref 和 cref。 这两函数返回一个持有到某特定类型的引用或 const 引用的 reference_wrapper。在前例中,要获得我们想要的语义,即使用同一个 keeping_state 实例,我们就需要把代码修改如下:
- int main() {
- keeping_state ks;
- boost::function<int(int)> f1;
- f1=boost::ref(ks);
- boost::function<int(int)> f2;
- f2=boost::ref(ks);
- std::cout << "The current total is " << f1(10) << '/n';
- std::cout << "The current total is " << f2(10) << '/n';
- std::cout << "After adding 10 two times, the total is "
- << ks.total() << '/n';
- }
boost::ref 的用途是通知 boost::function,我们想保存一个到函数对象的引用,而不是一个拷贝。运行这个程序有以下输出:
The current total is 10 The current total is 20 After adding 10 two times, the total is 20
这正是我们想要的结果。使用 boost::ref 和 boost::cref 的不同之处就象引用与 const 引用的差异,对于后者,你只能调用其中的常量成员函数。以下例子使用一个名为 something_else 的函数对象,它有一个 const 的调用操作符。
- class something_else {
- public:
- void operator()() const {
- std::cout << "This works with boost::cref/n";
- }
- };
对于这个函数对象,我们可以使用 boost::ref 或 boost::cref.
- something_else s;
- boost::function0<void> f1;
- f1=boost::ref(s);
- f1();
- boost::function0<void> f2;
- f2=boost::cref(s);
- f2();
如果我们改变了 something_else 的实现,使其函数为非const, 则只有 boost::ref 可以使用,而 boost::cref 将导致一个编译期错误。
- class something_else {
- public:
- void operator()() {
- std::cout <<
- "This works only with boost::ref, or copies/n";
- }
- };
- something_else s;
- boost::function0<void> f1;
- f1=boost::ref(s); // This still works
- f1();
- boost::function0<void> f2;
- f2=boost::cref(s); // This doesn't work;
- // the function call operator is not const
- f2();
如果一个 function 包含一个被 boost::reference_wrapper 所包装的函数对象,那么复制构造函数与赋值操作就会复制该引用,即 function 的拷贝将引向原先的函数对象。
- int main() {
- keeping_state ks;
- boost::function1<int,int> f1; // 译注:原文为boost::function<int,int> f1,有误
- f1=boost::ref(ks);
- boost::function1<int,int> f2(f1); // 译注:原文为boost::function<int,int> f2(f1),有误
- boost::function1<short,short> f3; // 译注:原文为boost::function<short,short> f3,有误
- f3=f1;
- std::cout << "The current total is " << f1(10) << '/n';
- std::cout << "The current total is " << f2(10) << '/n';
- std::cout << "The current total is " << f3(10) << '/n';
- std::cout << "After adding 10 three times, the total is "
- << ks.total() << '/n';
- }
这等同于使用 boost::ref 并把函数对象 ks 赋给每一个 function 实例。
给回调函数增加状态,可以发挥巨大的能力,这也正是使用 Boost.Function 与使用函数对象相比具有的非常突出的优点。
【Boost】boost库中function的用法相关推荐
- php function函数用法,js的function函数是什么?js中function的用法
本篇文章给大家带来的内容是关于js的function函数是什么?js中function的用法,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. Function与函数 Function是 ...
- Numpy库中einsum函数用法
Numpy中einsum函数用法 一.一维张量收缩 二.二维张量收缩 2.1 收缩到零维张量 2.2 收缩到一维张量 三.三维张量收缩(重难点) 3.1 例1 3.2 例2 四.其他功能介绍(次要) ...
- 【Boost】boost库中function和bind一起使用的技巧(二)
与 Boost.Function 一起使用 Boost.Bind 当我们把 Boost.Function 与某个支持参数绑定的库结合起来使用时,事情变得更为有趣.Boost.Bind 为普通函数.成员 ...
- 【Boost】boost库中function和bind一起使用的技巧(一)
1 bind/function 引 (1)头文件 bind函数#include <boost/bind.hpp> function使用头文件#include <boost/funct ...
- 【Boost】boost库中bind的用法
头文件: boost/bind.hpp bind 是一组重载的函数模板. 用来向一个函数(或函数对象)绑定某些参数. bind的返回值是一个函数对象. 它的源文件太长了. 看不下去. 这里只记下它的 ...
- Boost C++ 库
http://zh.highscore.de/cpp/boost/frontpage.html Boost C++ 库 目录 第 1 章 简介 第 2 章 智能指针 第 3 章 函数对象 第 4 章 ...
- linux怎么删除代码库,是否有从代码库中删除第三方C和C库的好技巧或工具? (OS X或Linux)...
我正在减少和隔离我对某些库的使用.我编写的许多现有程序直接使用这些库.我想要编译器(在这种情况下是GCC和/或Clang)或一些工具来帮助我识别我的代码库中的这些用法.简而言之,我想在代码库中毒害这些 ...
- 使用 Boost.Lambda 库创建并存储在 Boost.Function 对象中的函子中使用 Boost.Units 的数量、单位和绝对类型
使用 Boost.Lambda 库创建并存储在 Boost.Function 对象中的函子中使用 Boost.Units 的数量.单位和绝对类型 实现功能 C++实现代码 实现功能 使用 Boost. ...
- Boost库之function的使用
http://www.cnblogs.com/hujian/archive/2009/06/04/1495813.html Boost库的function是一组函数对象包装类的模板,实现了一个泛型的回 ...
最新文章
- 关于2D互动技术的一些要点
- 无法解决 equal to 操作中 SQL_Latin1_General_CP1_CI_AS 和 Chinese_PRC_CI_AS 之间的排序规则冲突。...
- 可能是基于 Hooks 和 Typescript 最好的状态管理工具
- 关联规则算法php,科学网—加权关联规则权值的计算 - 叶文菁的博文
- C语言 十进制整数字符串转十六进制字符串
- 12.Linux之输入子系统分析(详解)
- LeetCode 1744. 你能在你最喜欢的那天吃到你最喜欢的糖果吗?(前缀和)
- 图像的抽线、抽丝、抽图 神马是alpha通道
- Mac动态壁纸应用Dynamic Wallpaper 11
- Whatsns_V6.03互亿无线短信插件安装说明
- html 左侧居中对齐,HTML的居中对齐
- Codeforces Round #612 (Div. 1) A. Garland(dp动态规划)
- “无边框”引发口水大战 供应链考验手机硬件创新
- 字节跳动否认完成支付牌照收购,但金融野心一直有
- APP性能测试_帧率测试
- sybase监控执行sql(转自新浪)
- 大数据笔记8—java基础篇4(面向对象-封装-继承-多态)
- 算法岗面经总结(京东)
- 以太坊智能合约 —— 最佳安全开发指南
- Serializable接口和Parcelable接口