西西河

主题:庆祝Python跃居世界第四程序语言 -- 空格

共:💬100 🌺284
分页树展主题 · 全看首页 上页
/ 7
下页 末页
        • 家园 C++的复杂度并不是由于VC/MFC的关系吧

          对大多数程序员来说,从gcc开始比VC要更复杂。VC好歹还有部分WYSIWYG的功能,gcc对于大多数小白程序员来说属于学习曲线极陡的编译器。事实上,正是有一个学习曲线相对缓和的多的VC,C++的用户群才能扩大。C++的复杂度本身在于他横跨过程范式,对象范式和Generic Programming多种范式,同时还得保持与C的一定程度的兼容。实际上总体而言,C++属于精英语言,真不是培训班能培训出来的。有个比喻,C++如一把大刀,如果是个小孩子使用,非但发挥不了作用,反而可能伤到自己。C++的传世作品多,但出来的垃圾更多。相比诸如java和c#,跟使用人的水平不是没关系,但其关联程度不像C++那么大。

        • 家园 C++过度设计了

          C++想兼顾性能和语法的简洁,结果搞出难学习、难实现、易出错的语法标准。

          比如说重载。这玩意就为了程序员看起来方便,引入了极其复杂的name mangling机制,这一隐式转换的过程又没有被标准化,弄得各个编译器编译出来的东西互不兼容,C++的库也没有办法向后兼容到C,结果又必须引入export关键字。它看起来使程序员做的事情少了,实际上为了理解到底调用了哪个重载函数他们必须花费更多的时间,也增加了出问题的可能。其实这一过程应当能显式地控制,用类似C1X里面的type-generic macros,把重载的表显式地写在程序里面。

          更不要说模板了。这玩意一引入,哪个地方写错一个字符,哗啦哗啦好几页的出错信息。C++标准里面也没有对模板调试的内容,弄得IDE也处理不了,编译器出来的东西也没法读。

          光讨论设计上怎么自洽,怎么简洁,不考虑如何去实现它,也不考虑他的客户也就是广大程序员会怎么用,这是设计者的失败

          • 家园 说点不同意见

            重载,尤其是运算符重载,更尤其是圆括号()的重载是C++的一大优势。Functor的运用1)使得callback非常容易实现,和扩展。与C语言的函数指针相似,对被调用函数名称无限制,而更进一步增加了类型安全。2)与模板相结合,使得C++有能力与时俱进,在Function Programming流行的今天继续占有一席之地,比如boost.phoenix。boost.spirit更是运算符重载的杀手级运用,到目前为止我还不知道有哪种语言可以这样高效,无缝的内嵌表达式解析器,而且接口如此简单parse(target range,grammar,output).

            • 家园 C++的重载可以做到这个吗

              f(0){return 1;}

              f(n){return n*f(n-1);}

              • 家园 可以

                但必须引入template.

                因为C++是一种强类型语言,重载一般是指1)参数的个数不同或者2)参数的类型不同.

                此例如果0与n类型一样,不满足的以上重载适用的条件。 但是1)引入模板可以使得整数(integral)可以变成类型(type),2)对圆括号()运算符重载可以使类(class)变成函数。

                所以可以这样:

                template<int n> struct f

                {

                size_t operator()()

                {

                return f<n-1>()*n;

                }

                };

                template<> struct f<0>

                {

                size_t operator()()

                {

                return 1;

                }

                };

                这么使用:

                assert(f<0>() == 1);

                assert(f<1>() == 1);

                assert(f<2>() == 2);

                assert(f<3>() == 6);

                虽然形式上有点差异(有尖括号,而且参数在尖括号里),但思想本质上是一致的:即根据数值而不是类型重载,不知这是不是你想要的效果。

            • 家园 我也并非完全反对重载

              首先,我反对的是在所有域中不受限制的隐式重载。从实用主义的角度出发,类成员函数的重载是可以接受的,因为类成员函数相对于C来说是新的,可以在上面附加其他的功能,而且,类成员函数明确定义在一个域中,无论是功能上还是在代码上它们都是聚集的,并且限制在某个范围内,所以重名也不会引发理解上的问题。但是,对一个global scope里面的函数,隐式的重载使得其编译出来的代码和传统的C不兼容,而且会引发理解上的困难。例如:

              #include <foo2.h>

              void foo(float bar) {}

              foo(1);

              请问这个foo调用的是哪一个函数? 看起来它应当就是离它最近的那个函数,这理应是最简单的行为,但是C++中,如果foo2.h中有另一个void foo(int),那么它会调用另一个函数。这要求程序员和IDE都要做全局的分析以后才能知晓。如果我们禁止隐式重载,让重命名foo函数变为错误,而引入一个关键字overload做显式重载,那么会怎么样呢:

              #include <foo2.h>

              void foo_f(float bar) {}

              overload foo foo_i;

              foo(1);

              不动的部分保持了和C的兼容性,foo_f, foo_i都被直接地暴露到名字表中,使得我们可以去掉export关键字,而且增加的overload关键字在程序中显式地标识出重载表,考虑到它可以重复声明,因此可以在离调用最近的地方重声明来方便程序的理解。同理C++中的exception等都可以用类似的方法加入,而不需要修改整个语言。这样做的一个额外的好处就是如果程序员坚持老的方式,他不需要重新学习什么东西,不需要改变自己来适应语言的变化。

              其次,我反对C++现在这样的运算符重载。我认为Python的运算符重载方式更为优越。对于一个类,除非用operator关键字声明,除非在C++中使用运算符,否则一个程序员无法用别的方式重新定义和使用这一功能。所以C++的书里面必须单独辟出一章来讲运算符重载。其实运算符重载只不过是成员函数重载的一种特殊形式。如果我们强制规定类的__exec__方法对应operator (),那么,程序员就不需要额外学习运算符重载,他可以持续地按普通成员函数的方式定义和使用__exec__。而且,对于非C++的语言,也可以通过调用__exec__方法来复用C++程序员的成果。

              Boost.spirit这样的模板库,我还是那个看法,看起来很精致,很美,但是不实用。在缺乏编译过程的调试工具的现状下,使用复杂的模板库,其开发时间会远远超过使用非模板库。而且就算神的代码一次性正确又如何,编译时间都会使得基于它的代码缺乏竞争力。很多东西都不需要我们来批判它,它会自然地被时间淘汰的。

              通宝推:铁手,

              本帖一共被 1 帖 引用 (帖内工具实现)
              • 家园 你举的例子里之所以会产生混淆,与其说是因为

                overload,还不如说是因为隐形的类型转换(Implicit conversion),即int被允许自由的转换成float。如果不允许int隐形转为float,你如果想调用void foo(float),只能写成foo(1.0)或者foo(static_cast<float>(1))。而如果code里写成foo(1),读你的code的人必然意思到还有一个函数void foo(int)存在。这个隐形的类型转换是C的几个最被人指责的缺点之一。C++中为了和C兼容而不得不支持。在具体的工程开发中,比如我所在的公司,禁止使用隐形的类型转换。

                运算符overload最重要的应用是在generic programming。可以说如果不支持运算符overload,你不可能实现generic programming。

          • 家园 语法是很末节的东西

            而且我不觉得C++是在追求语法简洁,而是趋向于引进更多的功能。

            重载的潜在危险是对C++诟病的标准论点之一了,但我个人这些年还想不起来哪次因为重载而造成程序混乱,不论是我自己还是别人的程序。所以我觉得,危险有,但是过度夸大了。实际上重载所应用的场合是比较有限的,有些地方,比如数学运算,如果没有算符重载写起来是很痛苦的。

            对模版的看法就完全不同意了。调试起来的确不容易,但是从功能上说,正是因为模版,C++老树又发新芽,整个上了一个层次。STL在不牺牲效率的前提下,实现了通用数据结构和算法。而且因为模版对强类型的支持,经过编译优化的通用算法速度可以超过C。JAVA就是在模版上栽了大跟头。

            C++就象一大车的工具,有些杂乱地堆在一起。程序员选择的自由度很大,因此如果滥用潜在的风险也大。它改进的空间当然是很多的,但目前及短期内,它所提供的性能与功能还没有其他语言能够完全取代。

            • 家园 讨论一个特性的得失应当看它的机会收益

              首先这里有一个各个编译器的name mangling方式的表格:http://en.wikipedia.org/wiki/Name_mangling#How_different_compilers_mangle_the_same_functions

              这充分显示,重载的引入不是危害到单个的程序员,而是危害到整个C++社区。它导致:C++的函数库,如果不经特别处理,只能在C++里面。未处理的动态库里面的函数,C不能使用,Python不能使用,甚至连C++自己也只能由使用同一编译器的同一版本编译的代码来使用。微软当年搞COM的一个直接的原因就是C++ ABI层次上的不兼容,否则RPC的方案就足够了。这使得整个C++社区和Java社区那样,他们的成果很难在其他社区推广,而其他社区为了实现同样的功能,只能再去开发一套C的版本。

              如你所说,重载的实际使用是很少的。所以让任一函数皆可重载这一设计的代价已经远远超过了它的收益。这一设计只不过是满足学院派对于语言内部自洽性的个人偏好。允许大部分的函数不重载直接出现在名字表中以向下兼容,同时提供特殊的语法来显示地解决重载问题,现在看起来才是最合适的。

              同时我并不反对引入泛型。我只是不清楚Lisp里面两三页纸能讲清楚的问题,C++为什么要用好几本书。本来是很简单的一件事,可是有人却试图让所有的地方都适应它,同时为了简洁又不愿意多引入一些可以让它方便化的工具,最后就成了一个备受诟病的极其复杂的设计。

              其实泛型完全可以变得更为简单。举个例子吧,我们经常看到极长的尖括号用法。为什么不能缩写一点呢。原因是,最简单的缩写方法是宏,而有些人已经意识形态化地把这个东西批判到死了,重新谈宏方法的简洁有效性,不是扇自己巴掌么。剩下的方法是typedef,可是他们又不允许去typedef一种模板。现在怎么办,他们只能用极其繁复的方法去搞什么"泛型编程",用一种初学者看起来高高在上云里雾里的方法去实现本来很简洁的功能。每次看到这些模板库实现,我都想狠狠地抽一下这些人,他们让很多人为他们的无知和偏执付出代价。

              • 家园 你的思路在你的帖子里前后并不统一

                在overload上,你反对灵活,不允许任意overload,希望有更加严格的控制,比如支持显示的overload表达。在泛型上,你则完全相反,讨厌现行的严格准确的声明范型的方法,而希望引入更灵活的宏来声明范型。这里你又看重简介高效了,而不去考虑由此带来的各种问题,比如可读性差,编译困难等。所以,关键问题是你到底希望C++是一种什么样的语言?

              • 家园 c++真是个大酱缸,引入太多特性,太臃肿了
              • 家园 C++ ABI不兼容确实是个大问题,但是不光是重载引起的

                C++ 每引入一个新概念,ABI就要改一次。不光是重载,还有exception, RTTI等等,都需要修改ABI才能支持。所以用c++最好不要编译成动态链接库的形式,要么用静态链接,要么用extern "C", 只提供C语言风格的入口。

                C语言相对简单,其ABI的升级已经基本完成,除非重大升级比如libc.so.5到libc.so.6; 不像c++, g++4.1和g++4.2编译出来的.so都不能混用。

                C++混到现在这个尴尬的地位,和高高在上的C++标准委员会有极大的关系。这帮人一是行动缓慢,二是沉迷于语法细节,以繁琐为乐。我怀疑这个委员会是不是欧洲人占主体所以才这样。美国人采用的是实用主义哲学,以简单好用为追求,比如C, 比如TCP/IP; 欧洲人以精确严谨为追求,不在乎用户接不接受,比如C++,比如ISO-OSI七层协议。

                • 家园 我非常赞同你对C++标准委员会的看法

                  我认为overload和exception都有可以向下兼容C的实现方式,那就是,新特性新办法,老特性老办法,对C里面已有的东西要求引入额外的声明来显式地扩展:

                  http://www.ccthere.com/article/3313675

                  那堆人过于追求语言层面的精致,而不考虑他们的客户,也就是程序员,怎么在实际工作中去使用他们的产品

分页树展主题 · 全看首页 上页
/ 7
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河