您的位置:首页 > 编程语言 > C语言/C++

你注意到C++的函数对象都是传值的形式了吗?---boost::ref的强大用处~

2010-12-28 09:54 429 查看
如果你经常使用STL算法,那么你会注意到函数对象的传递都是传值的形式如下面的sort,for_each,_Compare__comp而不是_Compare&__comp传递引用。

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

http://aszt.inf.elte.hu/~gsd/halado_cpp/ch11.html

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
章节导航