资源描述
BOOST应用初探
作者:冯刚
2011-8-4
1 背景
BOOST是由c++标准化委员会成员参与开发的c++标准的准官方库,他是C++标准库的发动机。下一代C++标准C++0x的标准库很多来自于boost。Boost库覆盖了广泛的领域,从数学库到智能指针,从模板元编程库到预处理器库及语法词法分析,从线程到lambda表达式,等等。所有Boost库都具有宽松的许可证,确保库可以被自由使用于商用软件。
2 功能
2.1 概况
Boost库涵盖了很广泛的领域,下面就常用的功能像智能指针,正则表达式,函数对象bind和function, 线程和线程池,时间日期,多索引容器,哈希容器,bimap,日志,内存池,模板元编程,循环缓冲区,tuple, 观察者模式signal2,网络通讯asio做一些介绍。
2.2 功能
2.2.1 智能指针
2.2.1.1 概论
smart pointers(智能指针)是存储“指向动态分配(在堆上)的对象的指针”的对象。他们的行为很像 C++ 的内建指针,只是它们可以在适当的时候自动删除它们所指向的对象。智能指针在面对异常时有非常显著的作用,它们可以确保动态分配对象的完全析构。它们还可以用于跟踪多主人共享的动态分配对象。
在概念上,智能指针可以看作拥有它所指向的对象,并因此在对象不再需要时负责将它删除。
智能指针库提供了六个智能指针类模板:
scoped_ptr
<boost/scoped_ptr.hpp>
简单的单一对象的唯一所有权。不可拷贝。
scoped_array
<boost/scoped_array.hpp>
简单的数组的唯一所有权。不可拷贝。
shared_ptr
<boost/shared_ptr.hpp>
在多个指针间共享的对象所有权。
shared_array
<boost/shared_array.hpp>
在多个指针间共享的数组所有权。
weak_ptr
<boost/weak_ptr.hpp>
一个属于 shared_ptr 的对象的无所有权的观察者。
intrusive_ptr
<boost/intrusive_ptr.hpp>
带有一个侵入式引用计数的对象的共享所有权。
这些模板被设计用来补充 std::auto_ptr 模板的不足。
2.2.1.2 智能指针shared_ptr
shared_ptr 类模板存储一个指向动态分配对象(一般是用 C++ new-expression 生成的)的指针。在最后一个 shared_ptr 所指向的对象被销毁或重置时,要保证它所指向的对象被删除。
每一个 shared_ptr 都符合 C++ 标准库的 CopyConstructible 和 Assignable 的必要条件,并因此能够用于标准库容器。因为提供了比较操作,因此 shared_ptr 可以和标准库中的关联式容器一起工作。
通常,一个 shared_ptr 不能正确地持有一个指向动态分配的数组的指针。关于那种用法请参见 shared_array。
因为在实现中使用了引用计数,shared_ptr实例的循环引用不会被回收。例如,如果 main() 持有一个指向 A 的 shared_ptr, A 又直接或间接持有一个指回 A 的 shared_ptr,A 的使用计数是 2。最初的 shared_ptr 析构后将导致一个使用计数为 1 的 A 被悬挂。使用 weak_ptr 以“打破循环”。
这个类模板被 T 参数化,T 是被指向的对象的类型。shared_ptr 和它的大多数成员函数对于 T 没什么要求,允许它是一个不完整类型,或者为 void。对 T 有附加要求的成员函数 (constructors, reset) 都明确地记录在下面。
只要 T* 能被隐式地转换到 U*,则 shared_ptr<T> 就能被隐式地转换到 shared_ptr<U>。特别是,shared_ptr<T> 隐式转换到 shared_ptr<T const>,当 U 是 T 的一个可访问基类的时候,还能转换到 shared_ptr<U>,以及转换到 shared_ptr<void>。
惯用手法
由于智能指针是线程安全的,建议在代码里只为一个对象生成单一的shared_ptr,在多线程环境中传递参数时使用weak_ptr,这样当shared_ptr被析构,weak_ptr也会得到通知,变为无效,这样可以有效地防止野指针,同时也防止了潜在的循环引用,无法释放对象的问题。
shared_ptr<A> spA = shared_ptr<A>(new A);
int func(weak_ptr<A> wpA)
{
If(!wpA.expired())
{
//正常处理逻辑
shared_ptr<A> spA = wpA.lock();
//Use spA
}
else
{
//智能指针已被其他线程释放
}
}
func(spA);
2.2.1.3 智能指针weak_ptr
简介
weak_ptr是Boost智能指针shared_ptr的一个重要伙伴。它允许打破循环依赖。另外,它还可以处理一个非常常见的问题—悬空指针。当销毁最后一个智能指针shared_ptr,它会释放掉共享的资源。通过使用智能指针weak_ptr,这方面的信息会传播给所有观察该共享资源的智能指针weak_ptr,通过weak_ptr可以知道该共享资源已经释放,这意味着不会发生无意间访问无效指针的情形。这就是观察者模式的一个特例,也就是说,当销毁资源的时候,就会通知所有对资源感兴趣的weak_ptr,通过调用weak_ptr.expired(),当返回值为true时,就表示该共享资源已经释放了。
weak_ptr 类模板存储一个引向已被 shared_ptr 管理的对象的 "weak reference"(弱引用)。为了访问这个对象,一个 weak_ptr 可以利用 shared_ptr 的构造函数或成员函数 lock 转换为 shared_ptr。当最后一个指向对象的 shared_ptr 消失,而对象也被删除后,从一个引向已被删除对象的 weak_ptr 实例获取 shared_ptr 的企图就会失败:构造函数会抛出一个 boost::bad_weak_ptr 类型的异常,而 weak_ptr::lock 会返回一个 empty shared_ptr。
每一个 weak_ptr 都符合 C++ 标准库的 CopyConstructible 和 Assignable 的必要条件,并因此能够用于标准库容器。因为提供了比较操作,因此 weak_ptr 可以和标准库中的关联式容器一起工作。
weak_ptr 的操作绝不会抛出异常。
这个类模板被 T 参数化,T 是被指向的对象的类型。
相对于 shared_ptr,weak_ptr 提供了一个非常有限的操作子集,因为在多线程程序中访问它所存储的指针是非常危险的,甚至有时在一个单线程程序中也是不安全的(也就是说,它可能引起未定义行为)。姑且假设 weak_ptr 有一个返回 raw pointer(裸指针)的 get 成员函数,考虑下面这个无辜的代码片段:
shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
// some time later
if(int * r = q.get())
{
// use *r
}
设想在 if 之后,但是又恰恰在 r 被使用之前,另一个线程执行了语句 p.reset()。这样 r 就成了一个 dangling pointer(悬挂指针)。
解决这个问题的方案是从 q 创建一个临时的 shared_ptr:
shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
// some time later
if(shared_ptr<int> r = q.lock())
{
// use *r
}
这样,r 就持有一个引向 q 所指向的对象的引用。即使在其它线程中执行了 p.reset(),那个对象也会继续活着,直到 r 离开作用域或者被 reset。通过获得一个指向这个对象的 shared_ptr,我们可以有效地保住它不被析构。
2.2.2 正则表达式
xpressive 是一个先进的、面向对象的、用于C++的正则表达式模板库。正则表达式可以以字符串方式编写并在运行期分析,也可以以表达式模板方式编写并在编译期分析。正则表达式可以相互引用,或者递归引用其本身,你就可以用它们来构建任意复杂的语法。它与Boost.Regex库的区别就是无需编译,全模板库。
如果你要在C++中处理文本,通常你有两个不相交的选项:正则表达式引擎或语法分析生成器。正则表达式引擎(如 Boost.Regex)更为强大和灵活;文本的模式以字符串方式表示,可以在运行期指定。但是,这意味着语法错误同样要到运行期才能被检测。另外,正则表达式不适合于高级的文本处理任务,如匹配平衡的、嵌套的标签。那些任务传统上都是由语法分析生成器(如 Spirit 语法分析器框架)来处理的。这些工具更为强大,但不够灵活。它们通常不允许你随时随意地修改你的语法规则。此外,它们不具备正则表达式的完全回溯语义,对于某些类型的模式来说,这样对作者更具有挑战性。
将这两种方法无缝地集合到一起,在C++的文本处理世界中占据了独特的优势。通过 xpressive,你可以选择更象使用 Boost.Regex 那样去使用它,将正则表达式表示为字符串。或者你也可以象使用 Spirit 那样使用它,将你的 regexes 写为C++表达式,获得一种专用于文本处理的嵌入式语言所带来的所有好处。更重要的是,你可以混用这两种方式,从而获得两者的好处,对于那些要静态绑定的正则表达式编写正则表达式语法 -- 由编译器进行硬编码和语法检查 -- 其它的则动态绑定并在运行期指定。这些正则表达式可以相互递归地引用,在以前的正则表达式不能进行模式匹配的字符串中进行模式匹配。
Xpressive 是一个只含头文件的模板库,这意味着你不需要修改你的构建脚本或链接任何独立的lib文件就可以使用它。你所要做的就是 #include <boost/xpressive/xpressive.hpp>。如果你只使用静态 regexes,你可以只包含 xpressive_static.hpp 以提高编译速度。同样,如果你计划只使用动态regexes,则可以只包含 xpressive_dynamic.hpp。
表 28.1. xpressive的工具箱
Tool 工具
Description 说明
basic_regex<>
含有一个已编译的正则表达式。basic_regex<> 是xpressive之中最重要的类型。你用xpressive做的任何事情都要从创建一个类型为 basic_regex<> 的对象开始。
match_results<>, sub_match<>
match_results<> 含有 regex_match() 或 regex_search() 操作的结果。它就象一个存有 sub_match<> 对象的向量。一个 sub_match<> 对象含有一个已标记的子表达式(在Perl中又称为后向引用)。基本上,它只是一个迭代器对,代表了已标记子表达式的开始和结束。
regex_match()
检查一个字符串是否匹配一个regex。regex_match() 要成功,必须是整个字符串从头到尾匹配regex。如果你给了 regex_match() 一个 match_results<>,那么它会将所有找到的带标记子表达式写入其中。
regex_search()
查找一个字符串,以发现其中匹配regex的子字符串。regex_search() 将尝试在字符串的每个位置查找匹配,从头部开始,当找到一个匹配或者整个字符串找完时停止。使用 regex_match() 时,如果你给了 regex_search() 一个 match_results<>,那么它会将所有找到的带标记子表达式写入其中。
regex_replace()
给定一个输入字符串,一个regex和一个替代字符串,regex_replace() 通过将输入字符串中与regex相匹配的部分替换为替代字符串来构建一个新的字符串。替代字符串可以含有对带标记子表达式的引用。
regex_iterator<>
一个与STL兼容的迭代器,可以很方便地找到在一个字符串中与某个regex匹配的所有地方。解引用一个 regex_iterator<> 会返回一个 match_results<>。递增一个 regex_iterator<> 可以找出下一个匹配。
regex_token_iterator<>
类似于 regex_iterator<>,除了一点,解引用一个 regex_token_iterator<> 会返回一个字符串。缺省地,它返回与regex匹配的整个子字符串,不过它也可以被配置为每次返回任一个或整个带标记子表达式,或者甚至是不匹配 regex的部分字符串。
regex_compiler<>
一个用于 basic_regex<> 对象的工厂。它将一个字符串"编译"为正则表达式。通常,你不需要直接关心 regex_compiler<>,因为 basic_regex<> 类有一个工厂方法,其内部使用了 regex_compiler<>。不过,如果你需要做一些花哨的东西,如创建一个带有不同 std::locale 的 basic_regex<> 对象,你就需要显式使用 regex_compiler<> 了。
现在你应该对xpressive所提供的工具有了一些了解,你可以通过回答以下两个问题来找到合适的工具:
1. 你要用哪种类型的迭代器来遍历你的数据?
2. 你要对你的数据做什么操作?
弄明白你的迭代器类型
在xpressive中的多数类都是根据迭代器类型参数化的模板类。xpressive定义了一些常用的typedefs来让你可以更容易地选择合适的类型。你可以用下表基于你的迭代器类型来找到正确的类型。
下表 为 xpressive Typedefs 与迭代器类型
std::string::const_iterator
char const *
std::wstring::const_iterator
wchar_t const *
basic_regex<>
sregex
cregex
wsregex
wcregex
match_results<>
smatch
cmatch
wsmatch
wcmatch
regex_compiler<>
sregex_compiler
cregex_compiler
wsregex_compiler
wcregex_compiler
regex_iterator<>
sregex_iterator
cregex_iterator
wsregex_iterator
wcregex_iterator
regex_token_iterator<>
sregex_token_iterator
cregex_token_iterator
wsregex_token_iterator
wcregex_token_iterator
你要留意系统的命名习惯。这些类型经常要一起使用,所以命名习惯可以帮助你一致地使用它们。例如,如果你有一个 sregex,你就应该使用 smatch。
如果你用的不是以上四种迭代器类型之一,那么你可以直接用模板并指定你的迭代器类型。
示例
以下你将看到六个完整的示例程序。
检查整个字符串是否匹配一个regex
这是来自于"简介"一节中的示例。为便于查看,在此重复。
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
using namespace boost::xpressive;
int main()
{
std::string hello( "hello world!" );
sregex rex = sregex::compile( "(\\w+) (\\w+)!" );
smatch what;
if( regex_match( hello, what, rex ) )
{
std::cout << what[0] << '\n'; // whole match 整个匹配
std::cout << what[1] << '\n'; // first capture 第一次捕获
std::cout << what[2] << '\n'; // second capture 第二次捕获
}
return 0;
}
程序输出如下:
hello world!
hello
world
检查一个字符串是否包含匹配某个regex的子串
请留意在这个例子中,我们是如何使用定制的 mark_tags 来使得匹配的模式更可读。我们可以在稍后使用 mark_tags 来对 match_results<> 进行索引访问。
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
using namespace boost::xpressive;
int main()
{
char const *str = "I was born on 5/30/1973 at 7am.";
// 以名字定义一些定制的 mark_tags,比 s1, s2 等更有意义。
mark_tag day(1), month(2), year(3), delim(4);
//该regex查找一个日期
cregex date = (month= repeat<1,2>(_d)) // 查找月份...
>> (delim= (set= '/','-')) // 后跟一个分隔符...
>> (day= repeat<1,2>(_d)) >> delim // 和一个日期再跟一个分隔符...
>> (year= repeat<1,2>(_d >> _d)); //和年份。
cmatch what;
if( regex_search( str, what, date ) )
{
std::cout << what[0] << '\n'; // whole match 全匹配
std::cout << what[day] << '\n'; // the day 日期
std::cout << what[month] << '\n'; // the month 月份
std::cout << what[year] << '\n'; // the year 年份
std::cout << what[delim] << '\n'; // the delimiter 分隔符
}
return 0;
}
This program outputs the following:
程序输出如下:
5/30/1973
30
5
1973
/
替换匹配某个regex的所有子串
以下程序在一个字符串中查找日期并用伪-HTML标记它们。
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
using namespace boost::xpressive;
int main()
{
std::string str( "I was born on 5/30/1973 at 7am." );
// 基本上与上一个例子中的regex相同,但用的是动态regex
sregex date = sregex::compile( "(\\d{1,2})([/-])(\\d{1,2})\\2((?:\\d{2}){1,2})" );
// 和在Perl中一样,$& 是指向匹配该regex的子串的引用
std::string format( "<date>$&</date>" );
str = regex_replace( str, date, format );
std::cout << str << '\n';
return 0;
}
程序输出如下:
I was born on <date>5/30/1973</date> at 7am.
查找匹配某个regex的所有子串并每次一个地分步处理它们
以下程序在一个宽字符串中查找单词。它使用 wsregex_iterator。注意,对 wsregex_iterator 的解引用将产生一个 wsmatch 对象。
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
using namespace boost::xpressive;
int main()
{
std::wstring str( L"This is his face." );
//查找一个完整的单词
wsregex token = +alnum;
wsregex_iterator cur( str.begin(), str.end(), token );
wsregex_iterator end;
for( ; cur != end; ++cur )
{
wsmatch const &what = *cur;
std::wcout << what[0] << L'\n';
}
return 0;
}
程序输出如下:
This
is
his
face
将字符串分拆为匹配某个regex的记号
以下程序在字符串中查找比赛时间,并且先显示分钟数再显示秒数。它使用 regex_token_iterator<>.
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
using namespace boost::xpressive;
int main()
{
std::string str( "Eric: 4:40, Karl: 3:35, Francesca: 2:32" );
//查找比赛时间
sregex time = sregex::compile( "(\\d):(\\d\\d)" );
// 对于每个匹配,记号迭代器首先取出第一个被标记的子表达式的值,然后是第二个子表达式的值
int const subs[] = { 1, 2 };
sregex_token_iterator cur( str.begin(), str.end(), time, subs );
sregex_token_iterator end;
for( ; cur != end; ++cur )
{
std::cout << *cur << '\n';
}
return 0;
}
This program outputs the following:
程序输出如下:
4
40
3
35
2
32
用一个regex作为分隔符分拆字符串
以下程序接受一些带有html标记的文本,去掉其中的标记。它使用一个regex来匹配HTML标签,并用一个 regex_token_iterator<> 返回字符串中不匹配该regex的其余部分。
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
using namespace boost::xpressive;
int main()
{
std::string str( "Now <bold>is the time <i>for all good men</i> to come to the aid of their</bold> country." );
// find a HTML tag 查找一个HTML标签
sregex html = '<' >> optional('/') >> +_w >> '>';
// 以下的 -1 指示记号迭代器显示字符串中不匹配正则表达式的部分。
sregex_token_iterator cur( str.begin(), str.end(), html, -1 );
sregex_token_iterator end;
for( ; cur != end; ++cur )
{
std::cout << '{' << *cur << '}';
}
std::cout << '\n';
return 0;
}
程序输出如下:
{Now }{is the time }{for all good men}{ to come to the aid of their}{ country.}
显示嵌套结果组成的树
以下是一个辅助类,示范了如何显示由嵌套结果组成的树:
//以缩入方式将嵌套结果输出至 std::cout
struct output_nested_results
{
int tabs_;
output_nested_results( int tabs = 0 )
: tabs_( tabs )
{
}
template< typename BidiIterT >
void operator ()( match_results< BidiIterT > const &what ) const
{
//首先进行缩入
typedef typename std::iterator_traits< BidiIterT >::value_type char_type;
char_type space_ch = char_type(' ');
std::fill_n( std::ostream_iterator<char_type>( std::cout ), tabs_ * 4, space_ch );
//输出匹配结果
std::cout << what[0] << '\n';
//输出嵌套的匹配
std::for_each(
what.nested_results().begin(),
what.nested_results().end(),
output_nested_results( tabs_ + 1 ) );
}
};
2.2.3 Bind
目的
boost::bind 是标准函数 std::bind1st 和 std::bind2nd 的泛化。它支持任意的函数对象,函数,函数指针,和成员函数指针,它还能将任何参数绑定为一个特定的值,或者将输入的参数发送到任意的位置。bind 对函数对象没有任何要求,特别是,它不需要 result_type,first_argument_type 和 second_argument_type 这样的标准 typedefs。
int f(int a, int b)
{
return a + b;
}
int g(int a, int b, int c)
{
return a + b + c;
}
bind(f, 1, 2) 会产生一个“无参数”函数对象,它不需要参数并返回 f(1, 2)。同样,bind(g, 1, 2, 3)() 等价于 g(1, 2, 3)。
有选择性地只绑定一部分参数也是有可能的。bind(f, _1, 5)(x) 等价于 f(x, 5),这里,_1 是一个占位符参数,它的含义是“用第一个输入参数取代”。
bind 能够处理带有两个以上参数的函数,而且它的参数取代机制更为直观:
bind(f, _2, _1)(x, y); // f(y, x)
bind(g, _1, 9, _1)(x); // g(x, 9, x)
bind(g, _3, _3, _3)(x, y, z); // g(z, z, z)
bind(g, _1, _1, _1)(x, y, z); // g(x, x, x)
注意,最后一个示例中,由 bind(g, _1, _1, _1) 生成的函数对象不包含对第一个参数以外的任何参数的引用,但是它仍然能使用一个以上的参数。所有多余的参数被悄悄地忽略,就像在第三个示例中,第一和第二个参数被忽略。
bind 持有的参数被返回的函数对象拷贝并内部持有。例如,在下面的代码中:
int i = 5;
bind(f, i, _1);
一个 i 的值的拷贝被存储于函数对象中。boost::ref 和 boost::cref 可用于让函数对象存储一个引用而不是拷贝:
int i = 5;
bind(f, ref(i), _1);
bind(f, cref(42), _1);
(和函数对象一起使用 bind)
bind 并不限于函数,它可以接受任何函数对象。通常情况下,生成的函数对象的 operator() 的返回类型必须显式指定(没有 typeof 操作符,返回类型无法推导):
struct F
{
int operator()(int a, int b) { return a - b; }
bool operator()(long a, long b) { return a == b; }
};
F f;
int x = 104;
bind<int>(f, _1, _1)(x); // f(x, x), i.e. zero
有些编译器遇到 bind<R>(f, ...) 语法会发生问题。出于可移植性的原因,一种和上面的意思相同的可选的表达方式也被支持:
boost::bind(boost::type<int>(), f, _1, _1)(x);
但是要注意,这种可选语法只是作为一个 workaround 提供。它不是接口的一部分。
当函数对象暴露了一个名为 result_type 的内嵌类型时,显式返回类型可以被省略:
int x = 8;
bind(std::less<int>(), _1, 9)(x); // x < 9
【注意:这种省略返回类型的能力并非在所有的编译器上都可用。】
缺省情况下,bind 为提供的函数对象做出一份拷贝。boost::ref 和 boost::cref 可用于让它存储这个函数对象的引用,而非拷贝。当函数对象是不可拷贝的,拷贝代价高昂,或者包含状态时是很有用的,当然,在这种情况下,要求程序员确保这个函数对象在使用期间不能被销毁。
struct F2
{
int s;
typedef void result_type;
void operator()( int x ) { s += x; }
};
F2 f2 = { 0 };
int a[] = { 1, 2, 3 };
std::for_each( a, a+3, bind( ref(f2), _1 ) );
assert( f2.s == 6 );
(和成员指针一起使用 bind)
成员函数的指针和数据成员的指针不是函数对象,因为它们不支持 operator()。为了方便起见,bind 接受成员指针作为它的第一个参数,而它的行为就像使用 boost::mem_fn 将成员指针转换成一个函数对象一样。换句话说,当 R 是 X::f 的返回类型(作为成员函数)或成员本身的类型(作为数据成员)时,表达式
bind(&X::f, args)
与
bind<R>(mem_fn(&X::f), args)
等价。
【注意:mem_fn 创建的函数对象可以接受一个对象的指针,引用或智能指针作为它的第一个参数,更多的信息,参见 mem_fn 文档。】
示例:
struct X
{
bool f(int a);
};
X x;
shared_ptr<X> p(new X);
int i = 5;
bind(&X::f, ref(x), _1)(i); // x.f(i)
bind(&X::f, &x, _1)(i); //(&x)->f(i)
bind(&X::f, x, _1)(i); // (internal copy of x).f(i)
bind(&X::f, p, _1)(i); // (internal copy of p)->f(i)
最后两个示例的有趣之处在于它们生成“自包含”的函数对象。bind(&X::f, x, _1) 存储 x 的一个拷贝。bind(&X::f, p, _1) 存储 p 的一个拷贝,而且因为 p 是一个 boost::shared_ptr,这个函数对象保存一个属于它自己的 X 的实例的引用,而且当 p 离开它的作用域或者被 reset() 之后,这个引用依然保持有效。
(为函数组合使用嵌套的 binds)
传给 bind 的某些参数可以嵌套 bind 表达式自身:
bind(f, bind(g, _1))(x); // f(g(x))
当函数对象被调用的时候,如果没有指定顺序,内部 bind 表达式先于外部 bind 表达式被求值,在外部 bind 表达式被求值的时候,用内部表达式的求值结果取代它们的占位符的位置。在上面的示例中,当用参数列表 (x) 调用那个函数对象的时候,bind(g, _1)(x) 首先被求值,生成 g(x),然后 bind(f, g(x))(x) 被求值,生成最终结果 f(g(x))。
bind 的这个特性可被用来执行函数组合。参见示例 bind_as_compose.cpp,示范如何用 bind 达到与 Boost.Compose 类似的功能。
注意第一个参数——被绑定函数对象——是不被求值的,即使它是一个由 bind 生成的函数对象或一个占位符参数,所以下面的示例不会如你所愿地工作:
typedef void (*pf)(int);
std::vector<pf> v;
std::for_each(v.begin(), v.end(), bind(_1, 5));
你所要的效果,可以通过将一个辅助函数对象 apply 用作它的第一个参数而获得,作为一个函数对象,它可以支撑它的参数列表。为了方便起见,在 boost/bind/apply.hpp 头文件中提供了一个 apply 的
展开阅读全文