![]() ![]() |
|
神话与谬误:争论C++前你应当知道什么 | |
作者:佚名 文章来源:不详 点击数 更新时间:2008/4/18 14:39:23 文章录入:杜斌 责任编辑:杜斌 | |
|
|
哈雷将军的笑话想必大家都听过。一句话经口口相传,每个人都根据自己的主观意念加以润色,修补,歪曲…到最后就面目全非。这里最关键的一环就是主观意识,在历史学里面有这么一句话,大致意思是历史其实只存在于人的意念之中;就算完全客观的事件,通过不同的人的嘴说出来,造成的心理效应也往往不一样,每个人都会加上那么一两个形容词,驾驭语言能力高的更是能够舌绽莲花,而语言本就有自身的力量,其中的遣词造句对读者构成的心理影响力便应运而生。甚至于同一句话,用不同的语气说出来,都会造成不同的效果。同一句话,站在不同的立场上看,也会根本不是同一个意思。比如“C++还算是门不错的语言”,站在C++拥护者的角度听是在怜悯加诋毁C++,而站在C++反对者的角度听却是抬举了C++。 让我们先对“学院派”下一个定义好不好?先问你自己一个问题,你心目中对“学院派”的定义是什么? 以下是一些选项: 1. 倾向于理论美。2. 忽视实际编码中的constraints(如效率,模块性、可读性等等)。3. 倡导语言律师行为。4. 钻细节。5. … 我想如果我说C++语言设计强调理论美,所有学过C++的人恐怕都会笑了…正如Bjarne自己所说的,C++设计初期的Rule of Thumb之一便是“不要陷入到对完美性的固执追求中”;不过具有讽刺意味的是,后面你会看到,正是这样的一种哲学带来了今天对C++的这个误解。 我猜持这样一种观点的人大多对于学院派的定义都是模糊的,一般都介于“提倡钻语言细节并利用语言细节的做法”、“关注语言特性本身而忽略实际编码需求”、“对语言细节无休止的争论”等等之间。 所以,当有人说“C++==学院派”的时候,他的真实意思很可能是:“C++语言的阴暗角落太多,而且C++社群还有提倡对语言角落把握的潜在哲学,就连C++0x的进化也似乎更多关注语言特性,而那些语言特性根本就跟我们实际开发者脱节了…”等等。 首先得承认的是,在近一个十年的时间内,C++社群的确某种程度上建立起了一种对语言细节过分关注的心态,这种心态毫无疑问是错误的,但只有知道这个错误是如何来的,才能解开这个结。而且,就算一时解不开这个结,知道了原因之后才能保持理性的宽容态度,而不是乱发抱怨。一个理性的态度,更有助于良性发展。例如如果C++社群都能明白这种潜哲学从何而来,或许也就会渐渐走向更好的发展了。 我用一个例子来说明这一点:你平时遍历一个数组,或一个容器的时候是怎么做的? for(std::vector::iterator it = v.begin(); it != v.end(); ++it) {…} 这种做法很臃肿。其实你的逻辑是“对v中的每个元素,做…事情”,你知道大多数其它流行的语言中都有内建的for_each。那C++中就没有了吗?有。STL的for_each算法,于是你写: struct MyOp{void operator()(int& i){…}}; std::for_each(v.begin(), v.end(), MyOp()); 这个方案实际很差。一是你还是得写v.begin()、v.end(),二是你得为此定义一整个新类。三是这个新类并不在你使用这个新类(for_each被调用)的点上,因为局部类不能做模板参数。 你要的是lambda function: for_each(v.begin(), v.end(), <>(int& i){ …}); 可是C++98没有。 你要的是内建foreach: for(int& i : v) {…} 可是C++98没有。 鉴于循环结构是编程中最常出现的结构之一。这个问题其实还是比较恼人的,如果你觉得不恼人可能只是因为你适应性习惯了,这未必是好事。比如每次都要写std::vector::iterator就很让人恼火,如果我换个容器,就要修改一堆std::vector<…>。那用typedef行不行啊?行。可仍然还是需要写一次typedef,我很懒,我什么多余的无用代码都不想写。要知道,每多出一行无用的(并非因表达思想所需要才出现)的代码,就增加一点维护负担,这也正是为什么语言的表达力如此重要的原因。 再举个经典的例子:模板元编程。模板元编程有啥用?日常开发者八辈子估计也用不到。但真的吗?没错,日常开发者并不会直接用到。但是,由模板元编程支持的各个boost子库呢?被选入C++0x的TR1的各个子库呢(间接用到)?那日常开发者用不用学模板元编程呢?不用学,根本不用学,这么复杂的技术学什么呢?也就是点技巧上的东西。那为什么偏有人学呢?待会再说。 还有大量的例子就不一一列了。其实STL的traits技术已经能够说明问题了。如果你仔细看一看,你会发现,那些所谓的利用C++黑暗角落的技术,几乎无一不是出现在库开发里面的,而之所以出现在库开发里面,是因为库开发中的需求驱动的——为了开发出更好的库。难道你不想用更好的库? 哦,说到“更好的库”,肯定会有同学有意见了。
再举个实例,有同学说,我只要写简单的代码。问题是,简单不意味着单纯。简单意味着在更高抽象层次上面编程,后者是要靠好的库抽象才能达到的。借用《Extended STL》里面的一个例子: 那问题是,为什么发展到后来,“钻语言细节”成了社群的潜在哲学呢? 这其实是一个心理学上的问题,跟语言没有关系,跟C++的初衷更没有关系。从心理上,在同一个领域,如果另一个人比你懂得更多,你就会倾向于佩服他,这时另一个人懂的东西有多大的用处其实并不那么重要,人对自己不懂的东西总是有一种敬畏感的。 再来,当你面临两个问题,一个是如何建立一个高质量的库(大),一个是如何修正库里面的小bug(如vector里面某个成员函数的异常保证问题)。如果你有一份时间,你更倾向于把它花在什么地方?人在心理上总是倾向于走“捷径”的,体现在这个问题上面便是更倾向于对付耍点小聪明就解决的小问题,并获得甚至并不亚于解决大问题的成就感。小问题的另一个吸引人的地方在于它耗时短,更“趁手”,它不需要你闭关苦苦编码几个月弄出一个框架来而且还不一定能成。所以这就给人一种错觉,C++社群只知道争论枝节问题,不知道实干库。哦,不是错觉,这的确是大部分的现状,但这个现象其实并不仅仅止于C++社群,这是人心理的共性造成的,这也就是为什么无论在哪个语言社群你都会看到争论最多的都是些“小问题”的原因。(当然,无论在哪个学科,也还总是有牛人去啃难啃的骨头的。但这并不是广大民众的状况。) 以上种种原因共同造就了C++社群的这种心态。 另外,说到语言进化顺便说一句,语言进化的职责之一便是废黜繁复的技巧,取代以直接表达思想的语言特性。而C++0x真正在履行这一职责。 最后来说一说前面留下来的一个问题:为什么C++设计的初衷——“不要固执于完美”——某种程度上带来了这个局面呢? 因为正是因为这种理念的指导,有不少语言特性从理论上都是不完备的:比如有copy语意没有move语意(有左值引用没有右值引用),于是Alexandrescu用Mojo框架来解决;比如支持可变参数的函数调用却不支持可变参数的模板参数列表,导致用元编程来解决;比如不支持构造函数转发,导致必须factor出一个公共的initialize函数来;比如不支持强类型的enum,结果用一大堆宏结合类来解决;比如不支持initializer list,结果用复杂的模板技术来实现某种类似的初始化方式;比如不支持auto和typeof,结果用更复杂N倍的模板元编程技术来实现一个模拟;比如不支持内建的alignment指示,导致Alexandrescu在实现类型安全的union的时候用尽了模板元编程技巧;比如不支持内建的foreach,结果借助于诡异的语言角落实现了一个几近完美的模拟;比如不支持内建的concept,导致使用模板技巧来实现也算能用的concept检查…这个列表可以一再延长下去,C++中这样的示例太多了。C++的不完美导致了各种各样的技巧应运而生,哦,不,应该说,应实际需求而生。这从另一个侧面正说明了一点—— C++太需要进化了! 有同学说,我只要一个能用的库就行了。但问题是,标准库能随便吗?标准库之所以不能随便,是因为像这样受众极其广泛的库可是要负责任的——将会有百万千万行代码都依赖它。如果标准库里面有bug,将会出现几百万上千万行workarounds,这些workarounds依赖于库的bug,为了保持向后兼容性,标准库甚至都不能修正这些bug。就连STL这样漂亮的抽象,迭代器区间还是闯了祸。另一方面,如果只是需要一个能用的库,C++社区有大量“能用”的库。姑且不说boost里面的了。 关键的问题不是一门语言能做什么,因为说到能做什么,汇编什么都能做。而是“在某个特定的领域,哪门语言表现更好”,人们的需求几乎总是对着某个特定的领域的。后者才是真正matter的问题。 从这个角度看,C++的市场其实只在效率这一块。有人可能会说,那效率这一块有C啊。问题是,C的抽象机制太弱。写架构简单的应用,或者写一些核心的(如驱动程序),没有面向对象结构的程序,容易。完全可以用C。但涉及到大型系统,比如.NET基层架构,一些3D游戏。必须用到面向对象或基于对象编程的领域,C在代码组织和抽象方面的弱点就暴露出来了。比如用C和宏来实现所谓OO,就正说明了C的抽象机制的薄弱。 原因有两方面。一方面,正因为“偏科”,所以有些语言才能在它们擅长的领域做得更好,乃至做到最好。“偏执狂才能生存”。人们的需求几乎总是在特定领域的,你说这时候人是愿意选用一门专门为这个领域而生的语言(ruby),还是愿意用一门general-purpose的语言(C++)?另一方面,就算C++在抽象机制上进化到了非常好,乃至于能在某些特定领域也表现不菲的话,由于市场早就被别的语言侵占,别的语言已经有了成百上千万行的代码基,别的语言的库已经发展到非常丰富的程度,别的语言的相关人才教育已经一代又一代,所以结果还是没得拼。 其实,从另一个角度来说,C++何尝不也是一门偏执的语言呢?C++的偏执就是效率,C的偏执也是效率,但C++提供更好的抽象,因此在这一块(效率+抽象),C++比C有优势。 C++的领土已经铸成,另一方面,C++的领土在可见的未来也不大可能缩水了。这是C++的现实,这个现实,至少在Bjarne看来,也没什么不好,因为它正反映了C++当时设计的意图——更好的C。我们也不用赶鸭子上架,非拿C++和其它语言比——适用的场合本就不同,没得比。 Fallacy #4 … |
|
![]() ![]() |