你注意到C++的函数对象都是传值的形式了吗?---boost::ref的强大用处~
2010-12-28 09:54
429 查看
如果你经常使用STL算法,那么你会注意到函数对象的传递都是传值的形式如下面的sort,for_each,_Compare__comp而不是_Compare&__comp传递引用。
这里你是否会疑惑呢,为什么要用传值的形式而不用传递引用呢,毕竟函数对象区别于函数指针的一大优点是自身可以带有状态变量。如下面这个函数对象它是带有一个状态变量a的,其实可以带有更复杂更占内存的变量更多的变量,那么传值的方式肯定有很大的拷贝代价了。
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
template<typename_RandomAccessIterator,typename_Compare> inlinevoid sort(_RandomAccessIterator__first,_RandomAccessIterator__last, _Compare__comp) { }
template<typename_InputIterator,typename_Function> _Function for_each(_InputIterator__first,_InputIterator__last,_Function__f) { //conceptrequirements __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>) __glibcxx_requires_valid_range(__first,__last); for(;__first!=__last;++__first) __f(*__first); return__f; }
这里你是否会疑惑呢,为什么要用传值的形式而不用传递引用呢,毕竟函数对象区别于函数指针的一大优点是自身可以带有状态变量。如下面这个函数对象它是带有一个状态变量a的,其实可以带有更复杂更占内存的变量更多的变量,那么传值的方式肯定有很大的拷贝代价了。
structFunc { inta; Func() :a(0) { } voidoperator()(intx) { add(x); } voidadd(intx) { cout<<"a:"<<a<<endl; cout<<"this:"<<this<<endl; cout<<"add"<<x<<endl; a+=x; } };
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
另外有一点是用传引用的方式能够改变状态变量如下面的a的值,如果我们希望传递的方式就是引用方式呢,我们就是希望被传递的Func()能能够在传递给其它函数后被其它函数使用而且改变自身状态,其它函数使用之后它的自身状态希望是变化的。
例如我写过一个对于OTL读取数据库的封装。
template<typenameFunc> voidprocess(Funcfunc) { //process(func); LOG(INFO)<<Pval(sql)<<"\n"; try { otl_streamos(1024,sql.c_str(),conn);//连接好数据库,并读出数据 func(os); os.flush(); } catch(otl_exception&p) {//interceptOTLexceptions cerr<<p.msg<<endl;//printouterrormessage cerr<<p.stm_text<<endl;//printoutSQLthatcausedtheerror cerr<<p.sqlstate<<endl;//printoutSQLSTATEmessage cerr<<p.var_info<<endl;//printoutthevariablethatcausedtheerror } }
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
现在我有一个应用,读取数据库中的词并统计热门词,
structReadFunc { typedefstd::deque<Node>Deque; typedefstd::priority_queue<Node,Deque,std::greater<Node>>PQueue; typedefstd::tr1::unordered_map<string,int>HashMap; HashMaphash_map; PQueuem_pqueue; ch_convert::ChConverterm_converter; longlongm_keyNum; longlongm_keyCount; template<typenameStream> voidoperator()(Stream&os) { stringkey; intcount; intline_num=0; longlongkey_count=0; boost::array<char,2>seperator={'','\t'}; while(!os.eof()) { os>>key>>count; … } }
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
现在问题来了,见下面
voidrun() {//normal情况热门词统计 LOG(INFO)<<"Normalrun"<<endl; ReadFuncreader; run_(reader);//在函数里面m_dbreader.process(reader),这里我就希望reader中的记录热门词的m_queue就是要被改变!如果传值。。。ohmygod!!!! finalize_(reader); }
那么言归正传,为什么STL要是使用传值的方式呢??用于传递函数对象呢???我觉得是为了下面的原因
std::sort(begin,end,Cmp());
看到了吗,传递临时的函数对象,这个很常见!!我只要一行代码就OK。如果是传引用的形式呢_Compare&__comp,那抱歉,你不能这么写,因为临时对象不能传引用。。。
你要写成
Cmpcmp;
std::sort(begin,end,cmp);
很麻烦是吧呵呵,我还是希望传引用有办法两全其美吗?
有一个workaround,用const引用传递,这样可以用来传递临时变量了。
template<typename_RandomAccessIterator,typename_Compare>inlinevoidsort(_RandomAccessIterator__first,_RandomAccessIterator__last,const_Compare&__comp){}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
这个办法对于我上面提到的统计热门词也是试用的,我可以传递引用,并且改变我的reader内部状态了,但是编译器有些不高兴了,它会warning因为你是const的声明,你强制去掉了它的const呵呵。。。。
C++标准鼓励我们怎么做呢,比较for_each等接口都是按照传值写的啊。。。你如果用那些接口你就没办法传引用啦?
用boost::ref,kaka问题全部解决。
template<typenameT> voidsort(Tfunc) { func(3); } Funcfunc; func(1); cout<<"func.a:"<<func.a<<endl;//1 sort(boost::bind<void>(boost::ref(func),_1)); cout<<"func.a:"<<func.a<<endl;//4//状态改变了,我们传递的是引用~
注意C++标准的tr1也实现了,ref,bind,function等等但是我还没完全弄懂,似乎有问题通不过编译,所以当前还是建议暂时用boost的靠谱一些。。。呵呵
附注关于bind
boost::bindisageneralizationofthestandardfunctionsstd::bind1standstd::bind2nd.Itsupportsarbitraryfunctionobjects,functions,functionpointers,andmemberfunctionpointers,andisabletobindanyargumenttoaspecificvalueorrouteinputargumentsintoarbitrarypositions.Binddoesnotplaceanyrequirementsonthefunctionobject;inparticular,itdoesnotneedtheresult_type,first_argument_typeandsecond_argument_typestandardtypedefs.
Example:
intf(inta,intb)
{
returna+b;
}
intg(inta,intb,intc)
{
returna+b+c;
}
Usageofbind
bind(f,1,2)willproducea"nullary"functionobjectthattakesnoargumentsandreturnsf(1,2).Similarly,bind(g,1,2,3)()isequivalenttog(1,2,3).
Itispossibletoselectivelybindonlysomeofthearguments.
bind(f,_1,5)(x)/*isequivalentto*/f(x,5)
Here_1isaplaceholderargumentthatmeans"substitutewiththefirstinputargument."
Thesamewiththeolderversion:
std::bind2nd(std::ptr_fun(f),5)(x);
Morecomplexsolutions:
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)
Notethat,inthelastexample,thefunctionobjectproducedbybind(g,_1,_1,_1)doesnotcontainreferencestoanyargumentsbeyondthefirst,butitcanstillbeusedwithmorethanoneargument.Anyextraargumentsaresilentlyignored,justlikethefirstandthesecondargumentareignoredinthethirdexample.
Theargumenstarecopies.Ifwewanttousereferences,weneedhelperfunctions:
inti=5;
bind(f,i,_1);
bind(f,ref(i),_1);
Functionobjects
Thebindisnotlimitedtofunctions;itacceptsarbitraryfunctionobjects.Inthegeneralcase,thereturntypeofthegeneratedfunctionobject'soperator()hastobespecifiedexplicitly(withoutatypeofoperatorthereturntypecannotbeinferred):
structF
{
intoperator()(inta,intb){returna-b;}
booloperator()(longa,longb){returna==b;}
};
Ff;
intx=104;
bind<int>(f,_1,_1)(x);//f(x,x),i.e.zero
intx=8;
bind(std::less<int>(),_1,9)(x);//x<9
Example
classimage;
classanimation
{
public:
voidadvance(intms);
boolinactive()const;
voidrender(image&target)const;
};
std::vector<animation>anims;
template<classC,classP>voiderase_if(C&c,Ppred)
{
c.erase(std::remove_if(c.begin(),c.end(),pred),c.end());
}
voidupdate(intms)
{
std::for_each(anims.begin(),anims.end(),boost::bind(&animation::advance,_1,ms));
erase_if(anims,boost::mem_fn(&animation::inactive));
}
voidrender(image&target)
{
std::for_each(anims.begin(),anims.end(),boost::bind(&animation::render,_1,boost::ref(target)));
}
Referencewrapper
Theheaderboost/ref.hppdefinestheclasstemplateboost::reference_wrapper<T>,thetwofunctionsboost::refandboost::crefthatreturninstancesofboost::reference_wrapper<T>,andthetwotraitsclassesboost::is_reference_wrapper<T>andboost::unwrap_reference<T>.
Thepurposeofboost::reference_wrapperistocontainareferencetoanobjectoftypeT.Itisprimarilyusedto"feed"referencestofunctiontemplates(algorithms)thattaketheirparameterbyvalue.
Tosupportthisusage,boost::reference_wrapperprovidesanimplicitconversiontoT&.Thisusuallyallowsthefunctiontemplatestoworkonreferencesunmodified.
namespaceboost
{
template<classT>classreference_wrapper;
template<classT>reference_wrapper<T>ref(T&t);
template<classT>reference_wrapper<Tconst>cref(Tconst&t);
template<classT>classis_reference_wrapper<Tconst>;
template<classT>classunwrap_reference<Tconst>;
}
Implementationistrivial
template<classT>classreference_wrapper
{
public:
typedefTtype;
explicitreference_wrapper(T&t);
operatorT&()const;
T&get()const;
T*get_pointer()const;
private:
T*t_;
};
PrevUpNext
DefineyourownstreambufferHomeTuple
相关文章推荐
- 6 C++ Boost 函数对象
- 【C++】boost::bind和函数对象一起使用实现便捷的异步编程
- Boost.Bind用法详解(一) 2008-05-09 15:50:50| 分类: C++ |字号 订阅 Boost.Bind 为函数和函数对象提供了一致的语法,对于值语义和指针语义也一样。
- C++ boost 组件简介:函数对象及高级编程
- Boost函数对象 boost.bind boost.function Boost.Ref Boost.Lambda
- 从零开始学C++之STL(八):函数对象、 函数对象与容器、函数对象与算法
- C++ STL/ (12) 函数对象适配器
- c++ 父类成员函数的参数中有子类类型的对象 (前置声明解决)
- c++之函数对象
- C++中的函数指针和函数对象总结
- C++利用类静态变量,实现伪类对象空指针成功访问含有成员变量的成员函数而不崩溃
- 指向子类对象的基类指针调用非虚函数都是基类的函数吗
- C++函数对象与函数指针不同之处
- 作为一个c++而非c程序员,该弃函数指针投函数对象了
- C++中函数指针和函数对象的总结
- 刚刚注意到,windows的资源管理器,把zip文件直接当成文件夹的形式,可以直接展开目录层次,感觉很强大啊
- C++浅析——返回对象的函数
- 如果一个函数中有局部对象的存在, 那么它就一定会存在 C++ 的异常处理机制
- 【深度探索c++对象模型】Function语义学之成员函数调用方式
- C++函数对象