news 2026/5/9 22:15:43

C语言对话-14.The Bind That Ties

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言对话-14.The Bind That Ties

徐波 翻译

尽管亚洲船队的到来已是迫在眉睫,而且我们的高官们未就此事公开发表评论,但我们的生活却回复平静,几乎和以往一样普通。谣声渐起,且有蔓延之势,但外星文物的发掘和研究工作仍有条不紊地进行着。

珍妮和我正手忙脚乱地修补一个破裂水管的分枝。一根“Y”水管的一个分枝突然破裂,水迅猛地喷出,把她淋了个落汤鸡。“快过来帮忙!”她大声叫喊,试图用一只人工封口将它盖住,防止水喷射,但明显无济于事。

我赶紧拿了一只夹子,把封口夹住,然后迅速将封口固定住。夹子被适当地与封口固定牢后,水注只剩下几道小水流,接着就止住了,大功告成。现在“Y”形水管的一个分枝已被封死,水只能流向另外一个分枝。

“是不是又让你想起了你的第一个工作。”珍妮边说边用毛巾擦着蓬乱的头发。

我咧嘴一笑,“确实如此。尝试过太多的选择后,只剩下一个…”

Guru正对我最近摆弄过的一些代码进行回顾。一切都还正常,直到她遇到下面这个函数:

void f(std::vector<int> &vect)

{

std::vector<int>::iterator firstOne;

for (firstOne = vect.begin();

firstOne != vect.end();

++firstOne)

{

doSomething(*firstOne, "Some string literal");

}

}

Guru抬头看看会议桌对面的我:“我的孩子,这个函数并无必要。你可以用受宠的for_each函数来完成同样的功能。”由于还有个学生也在席,所以她用词严肃。我表示歉意,看到那个可怜的学生的脸上流露出几分惧意,又感到同情。

“嗯,”我低声说道,“是这样的,我试过for_each,但它需要一个只接受一个参数的函数。可我必须向doSomething传递两个参数,所以无法使用for_each。”

我的回答让她眉头更锁:“你有没有考虑过使用适配器(adaptor),象bind1st?”

我怯生生地申辩道:“是这样的,blindlst看上去总有点复杂,我怎么也想不出它“捆绑(bind)”参数是什么意思,所以,我属于那种……反正我没考虑用它。”

我以为Guru会摆出导师的架子,狠狠地瞪我。但她只是平静地点点头,嘴角写出淡淡的笑意:“我的孩子,当你面对你不理解的东西时,千万不可胆怯。必须承认自己的无知,勇敢地面对它,你才会长进。”她若有所思地停顿了一下。

“让我们从头开始,”她接着说,“为简单起见,我把bind1st和bind2nd简称为bind函数,这样你就可以把二元函数对象当作一元函数对象来使用。”

“所以,”我慢声说道,“你的意思是,因为doSomething函数接受两个参数,它是个二元函数,也就是说,我可以使用bind1st函数,把它转换成一个一元函数,是不是?”

“很好,我的学徒工。你走对了路子,但还不够深入。我说过bind函数允许你使用二元函数对象。特别地,bind函数使用的函数对象必须从binary_function派生,或必须提供跟binary_function一样的typedef形式。现在,有了这些信息,你该怎样修改你的作品来使用functor呢?”

有时候,我觉得Guru这种苏格拉底式的方法比我大学时的任何考试都要紧张,压力也更大。感受到她咄咄逼人的目光,我开始在白写字板上细心书写:

我听到Guru清了清嗓子,便扭过头去,她递给我一本Stroustrup的经典作品,我快速地查阅了一下binary_function:

template <class Arg, class Arg2, class Res>

struct binary_function {

typedef Arg first_argument_type;

typedef Arg2 second_argument_type;

typedef Res result_type;

}; [1]

“好家伙,一上来就出错。”我心想,把类的声明修改为:

class doSomething :

public std::binary_function<int, const char *, void>

{

public:

void operator()(int intVal, const char *c)

{

// ... 你要做的事 ...

}

};

我回头看看Guru。

“干得不错,”她赞许地点点头,“现在就剩下怎样使用bind函数了。其实也很简单,象bind1st(operation,value)这样的表达式告诉编译器将value捆绑到operation的第一个参数。换句话说,当编译器在for_each函数中调用operation时,它就把value作为第一个参数来传递,把已提领的(dereferenced)的iterator作为第二个参数,就象这样。”她拿起笔,在书写板中写道:

operation(value, *iterator)

“用这种思路来想问题,”Guru接着说,“使用bindlst后,第一个参数便被固定为某种特定的值,不会再改变。第二个参数在其遍历范围内依次传递给各个对象,只有它才会变化。这样,二元函数对象现在就可以象一元函数对象那样使用了。”

“就是这样?”我眨了眨眼睛,“这么简单?”

Guru点点头:“是的,我的孩子,就是这么简单? bind2nd也差不多,告诉编译器将给定的值捆绑到第二个参数。现在,给我看看你怎样替换或重写你的f函数。”

我在白书写板上写道:

std::for_each(vect.begin(), vect.end(),

bind2nd(doSomething(), "Hi"));

她点点头:“非常好,我的学徒工。你已经意识到传给bind2nd的第一个参数必须是你的functor的一个实例,这样,你就创建了一个未命名的临时对象。”我不好意思地笑了笑,我并不想假装括号是无意加上去的,我只是已经用惯了这种冗余写法。“我们的代码回顾结束时,”她接着说,“我希望你写一个小型测试程序来加深你的理解。”

代码回顾结束后,我回到我的蜗居,写了一个小程序。经过一些小小的完善后,最终的形式如下:

#include <vector>

#include <iostream>

#include <functional>

#include <iterator>

#include <algorithm>

void doSomething(int i, const char *c);

// 我的第一个二元功能函数,

// 首先,我假定doSomething是某个库函数,

// 我并没有它的源代码。

// 关于可移植性:MS VC6.0不喜欢在模板的返回类型中使用void,

// 所以在MS VC6.0中对operator( )稍作修改,使它返回一个类型(如true)

struct doSomethingWrapper : public

std::binary_function<int, const char *, void>

{

void operator()(int iValue, const char *cValue) const

{

doSomething(iValue, cValue);

}

};

// 现在,就建立了一个内部的功能函数。

// 关于可移植性,同上。

struct doSomethingDirect : public

std::binary_function<int, const char *, void>

{

void operator()(int iValue, const char *cValue) const

{

std::cout << cValue

<< " "

<< iValue

<< ". " ;

}

};

// 这是个帮助器模板,因为我比较懒,它能减少打字量。

template <class Collection, class Function>

Function for_all(Collection &c, const Function &f)

{

return std::for_each(c.begin(), c.end(), f);

}

int main()

{

// 首先,建立vector。

std::vector<int> vect;

for (int i=0; i<10; ++i) {

vect.push_back(i);

}

for_all(vect, std::bind2nd(doSomethingWrapper(), "Wrapper:"));

std::cout << "\n";

for_all(vect, std::bind2nd(doSomethingDirect(), "Direct:"));

return 0;

}

// 我独树一帜的第三方库函数

void doSomething(int i, const char *c)

{

std::cout << c << " " << i << ". " ;

}

“这里面再加上个基于容器的算法,真是匠心独运啊!”我高兴地祝贺自己,权当自己是第一个想到该主意的人,事实上当然不是这样。

“这也很有独创性啊,”我叹了口气,拍了拍已经安静下来的水管分枝,“可惜我们需要两个分枝都能工作,唉!只能把整个都换掉了……”

我们面面相觑。

“明天吧。”珍妮一边拧她制服上的水,一边说道。

[注释]

[1] Bjarne Stroustrup. The C++ Programming Language, 3rd Edition (Addison-Wesley, 1997).

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 5:50:16

深度学习框架 基于深度学习YOLOv12的杂草识别检测系统(YOLOv12+YOLO数据集+UI界面+登录注册界面++模型) 高精度智慧农业杂草识别系统

基于深度学习YOLOv12的杂草识别检测系统&#xff08;YOLOv12YOLO数据集UI界面登录注册界面模型&#xff09;针对农田杂草高效识别与精准防控的需求&#xff0c;本研究基于深度学习目标检测算法YOLOv12&#xff0c;开发了一套杂草识别检测系统。系统以12类常见杂草&#xff08;如…

作者头像 李华
网站建设 2026/5/4 5:23:33

救命神器2026最新!9款AI论文写作软件测评:本科生毕业论文必备

救命神器2026最新&#xff01;9款AI论文写作软件测评&#xff1a;本科生毕业论文必备 2026年AI论文写作工具测评&#xff1a;为何值得一看&#xff1f; 随着人工智能技术的不断进步&#xff0c;AI写作工具在学术领域的应用愈发广泛。对于本科生而言&#xff0c;撰写毕业论文不仅…

作者头像 李华
网站建设 2026/5/9 11:44:09

AI测试用例生成的数据驱动革命:从真实行为到精准覆盖

1. 数据驱动的核心价值重构 传统测试用例设计依赖测试人员经验&#xff0c;难以应对现代系统的复杂性。数据驱动模式通过采集真实用户行为数据&#xff08;如操作序列、输入分布、异常路径&#xff09;&#xff0c;构建动态测试模型。某电商平台实践显示&#xff0c;采用用户行…

作者头像 李华
网站建设 2026/5/9 6:58:01

2026年8款降AI率工具亲测推荐:知网维普双平台实测通过

2026年8款降AI率工具亲测推荐&#xff1a;知网维普双平台实测通过 67%。 这是知网给我论文的AI检测结果&#xff0c;但那篇论文是我熬了整整一周&#xff0c;查了四十多篇文献&#xff0c;一个字一个字敲出来的。看到这个数字的时候&#xff0c;离答辩只剩五天。 后来我用嘎…

作者头像 李华
网站建设 2026/5/4 6:56:22

程序员必备:3款软著材料生成工具实测,最快30分钟出稿

上个月帮朋友的创业公司申请软著&#xff0c;光是整理60页源代码就花了我一个周末。代码从各个文件复制过来&#xff0c;调格式、加页眉页码、删注释、凑行数&#xff0c;整个人都麻了。 后来发现有专门生成软著材料的工具&#xff0c;30分钟就能搞定我折腾两天的活。试了三款…

作者头像 李华
网站建设 2026/5/4 6:56:22

知网AIGC检测不通过?3招搞定,附避坑清单

知网AIGC检测不通过&#xff1f;3招搞定&#xff0c;附避坑清单 我改了三遍&#xff0c;AI率从45%涨到了62%。 是的&#xff0c;越改越高。因为我用豆包帮忙改写&#xff0c;结果AI改AI&#xff0c;换了种AI味道&#xff0c;知网照样能识别出来。 后来我换了个思路&#xff…

作者头像 李华