王朝网络
分享
 
 
 

boost.bind 如何 bind

王朝other·作者佚名  2006-05-27
宽屏版  字体: |||超大  

Boost.bind 好用么?当然好用,而且它也确定进入下一代的 C++ 标准了,也早就进了 TR1 了。回顾一下,它允许我们干这个:

#include <algorithm>

#include <iostream>

#include <string>

#include <vector>

#include <boost/bind.hpp>

using namespace std;

using namespace boost;

struct Person

{

Person(const string& name)

: name_(name)

{}

string Name()

{

return name_;

}

string name_;

};

int main()

{

typedef vector<Person> PersonList;

PersonList personList;

personList.push_back(Person("Ralph"));

personList.push_back(Person("Joy"));

personList.push_back(Person("Martin"));

PersonList::iterator iter =

find_if(personList.begin(), personList.end(),

bind(&Person::Name, _1) == "Ralph");

cout << (iter == personList.end() ? "Not found."

: iter->Name().append(" found."))

<< endl;

}

如果没有它我们怎么办呢?恕我鲁钝,我还没办法用 bind1st, bind2nd 之类办到同样的事,恐怕你也只有写一个完全没营养的 predicate 来达到目的了。

当然,用不了几次,你就会问(或者可能多数人是在第一次看到的时候就会问):它是怎么办到的?其实还不如问,如果换了你会怎么做?我们降低条件,想象自己 是 bind 的作者,在开始的时候舍弃一切细节,只要实现类似 bind1st 或者 mem_fun 的功能,该怎么做?我们把这个 bind 叫做 simple_bind 。

STL 的 bind1st 能给一点启示:它接受两个参数,返回一个 functor ,毫无疑问 simple_bind 也得这样干,我们把它返回的 functor 类型叫做 simple_binder:

template <typename R, typename T>

class simple_binder

{

public:

explicit simple_binder(R (T::*pfn)())

: pfn_(pfn)

{}

R operator()(T& t)

{

return t.*pfn_();

}

private:

R (T::*pfn_)();

};

这个东西跟 mem_fun_ref_t 可以相比,因为毕竟,算法是不变的,无论我们在外面玩什么花样,提供给算法的 functor 类型都必须长成这样。中间的 R (T::*pfn)() 是函数指针定义,的确,C++ 里面的函数指针定义从来就不可爱,所以才有了 boost.function ... 扯远了。

我们知道了 simple_bind 的返回类型,那么参数呢,第一个参数简单,跟上面的 pfn 是一样的,第二个是什么呢?我们先空起来:

template <typename R, typename T>

simple_binder<R, T>

simple_bind( R (T::*pfn)(), ??? )

{

return simple_binder<R, T>(pfn);

}

有没有发现什么?在这里我们压根没用到第二个参数,这就给了我们一个极其简单的定义:

class placeholder{};

placeholder _1;

那么现在程序好像是完整了,我们可以这么用:

#include <iostream>

#include <string>

using namespace std;

struct Person

{

Person(const string& name)

: name_(name)

{}

string Name()

{

return name_;

}

string name_;

};

template <typename R, typename T>

class simple_binder

{

public:

explicit simple_binder(R (T::*pfn)())

: pfn_(pfn)

{}

R operator()(T& t)

{

return (t.*pfn_)();

}

private:

R (T::*pfn_)();

};

class placeholder{};

placeholder _1;

template <typename R, typename T>

simple_binder<R, T>

simple_bind( R (T::*pfn)(), placeholder&)

{

return simple_binder<R, T>(pfn);

}

int main()

{

Person person("Ralph");

cout << simple_bind(&Person::Name, _1)(person) << endl;

}

输出是 Ralph ,跟直接调用一样!我们的 simple_bind 起作用了!

好了,现在让我们再向前一步,如果 Person 加了一个函数 SetName:

void SetName(string name)

{

name_ = name;

}

它有一个参数,而我们希望调用的形式类似于 simple_bind(&Person::SetName, string("Joy"), _1) 我们怎么修改 simple_bind 呢?

首先还是从 simple_binder 入手,由于调用方法不变,我们所要做的只是在 simple_binder 里面保存参数,没错,就跟 binder1st 一样:

template <typename R, typename T, typename Arg>

class simple_binder

{

public:

explicit simple_binder(R (T::*pfn)(Arg), const Arg& arg)

: pfn_(pfn)

, arg_(arg)

{}

R operator()(T& t)

{

return (t.*pfn_)(arg_);

}

private:

R (T::*pfn_)(Arg);

Arg arg_;

};

接下来的就顺理成章了,simple_bind 把 arg 传递给 simple_binder ,而 placeholder 甚至不需要修改,我们甚至可以把这个 simple_bind 直接喂给 STL 算法:

#include <algorithm>

#include <iostream>

#include <string>

#include <vector>

using namespace std;

struct Person

{

Person(const string& name)

: name_(name)

{}

string Name()

{

return name_;

}

void SetName(string name)

{

name_ = name;

}

string name_;

};

template <typename R, typename T, typename Arg>

class simple_binder

{

public:

explicit simple_binder(R (T::*pfn)(Arg), const Arg& arg)

: pfn_(pfn)

, arg_(arg)

{}

R operator()(T& t)

{

return (t.*pfn_)(arg_);

}

private:

R (T::*pfn_)(Arg);

Arg arg_;

};

class placeholder{};

placeholder _1;

template <typename R, typename T, typename Arg>

simple_binder<R, T, Arg>

simple_bind( R (T::*pfn)(Arg), const Arg& arg, placeholder&)

{

return simple_binder<R, T, Arg>(pfn, arg);

}

int main()

{

typedef vector<Person> PersonList;

PersonList personList;

personList.push_back(Person("Ralph"));

personList.push_back(Person("Joy"));

personList.push_back(Person("Martin"));

for_each(personList.begin(), personList.end(),

simple_bind(&Person::SetName, string("Joy"), _1));

cout << personList[0].Name() << endl

<< personList[1].Name() << endl

<< personList[2].Name() << endl;

}

输出是三个 Joy 。

现在看起来,我们的 simple_binder 稍微有点样子了,但是 boost.bind 允许 placeholder 被放在任何一个位置,而 simple_binder 不允许,但是这个简单,我们有重载:

template <typename R, typename T, typename Arg>

simple_binder<R, T, Arg>

simple_bind( R (T::*pfn)(Arg), const placeholder&, const Arg& arg)

{

return simple_binder<R, T, Arg>(pfn, arg);

}

加上这个,_1 就可以被随便放在第一个或者第二个位置,例如:

int main()

{

typedef vector<Person> PersonList;

PersonList personList;

personList.push_back(Person("Ralph"));

personList.push_back(Person("Joy"));

personList.push_back(Person("Martin"));

for_each(personList.begin(), personList.end(),

simple_bind(&Person::SetName, string("Joy"), _1));

for_each(personList.begin(), personList.end(),

simple_bind(&Person::SetName, _1, string("Ralph")));

cout << personList[0].Name() << endl

<< personList[1].Name() << endl

<< personList[2].Name() << endl;

}

输出是三个 Ralph 。提醒一下,boost.bind 当然不是用这个办法,要不然还不用等到参数数量到10个,到了3个我们就要累死。simple_bind 的目的只是在于探索,用最容易理解的方式达成目的,如果说 boost 是一座拆除了脚手架、精雕细琢的圣殿,simple_bind 就是尝试把脚手架再搭起来。

回顾一下,现在我们已经能比较好地解决一个参数的成员函数调用了,现在看看两个参数。我们从目的入手,希望达到的效果是这样:

Person person("Ralph");

simple_bind(&Person::SetName, _1, _2)(person, string("Martin"));

首先,这里有了一个新的符号 _2 ,这很简单

placeholder _2;

然后 simple_bind 也顺利成章的又增加了一个重载:

template <typename R, typename T, typename Arg>

simple_binder<R, T, Arg>

simple_bind( R (T::*pfn)(Arg), const placeholder&, const placeholder&)

{

return simple_binder<R, T, Arg>(pfn);

}

注意,这个 simple_binder 构造函数只有一个参数,而它还没写出来,我们加上一个:

// new simple_binder ctor: 1 argument

explicit simple_binder(R (T::*pfn)(Arg))

: pfn_(pfn)

{}

在这种情况下,两个参数都是由调用者直接提供给 simple_binder 的,这就意味着我们还要重载一次 simple_binder::operator() :

R operator()(T& t, const Arg& arg)

{

return (t.*pfn_)(arg);

}

好了,这下子都解决了,完整程序如下:

#include <algorithm>

#include <iostream>

#include <string>

#include <vector>

using namespace std;

struct Person

{

Person(const string& name)

: name_(name)

{}

string Name()

{

return name_;

}

void SetName(string name)

{

name_ = name;

}

string name_;

};

template <typename R, typename T, typename Arg>

class simple_binder

{

public:

explicit simple_binder(R (T::*pfn)(Arg))

: pfn_(pfn)

{}

explicit simple_binder(R (T::*pfn)(Arg), const Arg& arg)

: pfn_(pfn)

, arg_(arg)

{}

R operator()(T& t)

{

return (t.*pfn_)(arg_);

}

R operator()(T& t, const Arg& arg)

{

return (t.*pfn_)(arg);

}

private:

R (T::*pfn_)(Arg);

Arg arg_;

};

class placeholder{};

placeholder _1, _2;

template <typename R, typename T, typename Arg>

simple_binder<R, T, Arg>

simple_bind( R (T::*pfn)(Arg), const Arg& arg, const placeholder&)

{

return simple_binder<R, T, Arg>(pfn, arg);

}

template <typename R, typename T, typename Arg>

simple_binder<R, T, Arg>

simple_bind( R (T::*pfn)(Arg), const placeholder&, const Arg& arg)

{

return simple_binder<R, T, Arg>(pfn, arg);

}

template <typename R, typename T, typename Arg>

simple_binder<R, T, Arg>

simple_bind( R (T::*pfn)(Arg), const placeholder&, const placeholder&)

{

return simple_binder<R, T, Arg>(pfn);

}

int main()

{

typedef vector<Person> PersonList;

PersonList personList;

personList.push_back(Person("Ralph"));

personList.push_back(Person("Joy"));

personList.push_back(Person("Martin"));

for_each(personList.begin(), personList.end(),

simple_bind(&Person::SetName, string("Joy"), _1));

for_each(personList.begin(), personList.end(),

simple_bind(&Person::SetName, _1, string("Ralph")));

Person person("Ralph");

simple_bind(&Person::SetName, _1, _2)(person, string("Martin"));

cout << personList[0].Name() << endl

<< personList[1].Name() << endl

<< personList[2].Name() << endl;

cout << person.Name() << endl;

}

输出是:

Ralph

Ralph

Ralph

Martin

虽然实现丑陋了点,但是达到目的了,不是么?好了,现在新的问题来了,我们还希望可以这样:

simple_bind(&Person::SetName, _2, _1)(string("Joy"), person));

其实从这里可以看出来,在前面的实现中,placeholder 没有发挥任何作用,是真正的 placeholder ,然而在这里不同,我们至少希望 placeholder 能够携带某些编译期信息,以便让我们能在调用决策中选择相应的参数,换句话说,我们希望 placeholder _1 和 _2 除了名字不同,还能让编译器看起来有所区别,当然最简单的想法是:

class placeholder1{};

class placeholder2{};

placeholder1 _1;

placeholder2 _2;

然而模仿 MCD 的 IntToType 技术,这样是不是更优雅呢:

template <int I>

class placeholder{};

placeholder<1> _1;

placeholder<2> _2;

boost.bind 里面就是这么干的。有了这个,就可以依照 placeholder 顺序来重载:

template <typename R, typename T, typename Arg>

simple_binder<R, T, Arg>

simple_bind( R (T::*pfn)(Arg), const placeholder<1>&, const placeholder<2>&)

{

return simple_binder<R, T, Arg>(pfn);

}

template <typename R, typename T, typename Arg>

simple_binder<R, T, Arg>

simple_bind( R (T::*pfn)(Arg), const placeholder<2>&, const placeholder<1>&)

{

// 返回什么呢?

}

返回什么呢?我们可以定义一个 simple_binder2 ,它在 operator() 里面会交换其参数的位置,也可以修改 simple_binder,让它根据某些标志来干这件事……

我们还是就此打住吧!

不用我说你也看得出来,这种“暴力法”很 快就会让代码规模和程序复杂度膨胀到无法收拾的地步,是时候检讨我们的设计了。我们原先的设计其实是从最简单的情况出发,在需求增多的时候,我们用蛮力加以扩展,然而当规模进一步扩大,设计本身的局限就开始显现出来,现在应该从原先的设计中提取抽象,考虑更加灵活的设计了。

(待续...)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有