- 论坛徽章:
- 0
|
本帖最后由 幻の上帝 于 2011-10-18 20:07 编辑
回复 123# OwnWaterloo
>> template<typename T/*这个算占位符么?*/>
----
算,但局限性太大了。
1.这里T是typename……那么template class T<class>之类的呢。
2.模版实例化的phase是被明确规定的,照着现有的编译型实现来说,也就是语言的实现无权把template保留在最终的目标文件中,也无法有效地避免自身语义上没有改变的模版被重新编译。
====
>>对此lisp程序员感到毫无压力……
----
就是照着Lisp说的……不过前提是得适应一堆括号……= =
其实我觉得Lisp的潜力远远不止作为一种programing language。可惜没什么NB的人来实现并引起足够的关注。
====
>>如果将编译+链接+加载作为语言提供的服务之一呢?
----
那也可以,如果标准能下决心保证一个实现必须提供这样的服务的话。
====
>> 但对于后一个例子, intergal_constaint , 这算是兼容性问题吧? 这东西先在 C++11 之前(没有constexp) 的 boost 中出现了, 难道还被标准化了?
----
integral_constant 真被标准化了……而且被其它部分用到,比如tuple_size就是继承了它的实例。
兼容性是重要的原因,不过我这里更想说的是核心语言特性的时候对可能造成的冗余关注得不够。
====
>> 或者换个例子。我是想问问兼容性问题有什么处理方法。
在C的年代,可能会有其他方案,但应该想不出这方案有什么不好。
直到:
template<typename T, typename U>
/*这里写什么?*/ f(T x, U y);
所以又不得不引入新的语法。但为了兼容性又得支持旧语法 —— 于是语言复杂了、新手迷糊了,为啥这也可以那也行……
完全向后兼容的软件,多演化几个版本,就会越来越臃肿 —— 反例一下子想不出来,正例应该不需要举了。
怎么办。。。
----
……或许是我不够乐观,或许真没办法了。也许自C开始(或者更早,BCPL,但没有很流行,可以不算)就注定无可救药了。
无论是设计软件还是语言,精确了解未来的需求需要非凡的前瞻能力。这种时间跨度上大家都做不好,也没办法。
====
>> 你觉得不好的地方是哪个呢:
1. C++03主要是靠析构函数来完成回滚的吧?
{ T g;
} // 这里有 ~T() 的调用
这算是运行期确定还是编译期?
2. const引用提升临时变量生存期
T makeT();
{ T const& g = makeT();
} // 这里有 ~T() 的调用,编译or运行?
3. 派生类到基类引用
D makeD();
{ B const& g = makeD();
} // 这里有 ~D() 的调用,编译or运行?
4. scope guard比上面再增加了一个成员,可以取消~T()中对某个函数的调用……
其实我对scopeguard评价很高的……
insert(pair<int,string>(12,"26")); 比较困难,但可以写
insert(make_pair(12,"26"));
find_if(beg, end, binder< ... —— 不查资料我真忘这里该怎么写了
find_if(beg, end, bind1st(less<int>(), 12) ); 虽然依然别扭,但还是可以写出来的
即使利用函数模板对参数的推导,省去书写类型的麻烦。
但这只对临时变量有用,而临时变量的生命周期太短……
当我想实现一个更范化的rollback方式(为每个需求都写一个class与析构太麻烦),真的就盼着auto赶紧被支持……
直到看见scopeguard…… 妈的!原来可以这样!!!
----
我感到火大的地方不是scope guard本身。事实上我认为scope guard是了不起的创造,但不得不使用这样的形式,说明用户正在受制于某些可以忍受却实质上扭曲的语言的语义规则之下——尽管大部分人没意识到。
不管怎么说,scope guard没有直观到是一般人学着学着就能自己发明出来的轮子。而它的功能却是非常直观且作用显著的,我故意讲的抽象点,也就是——协助完成程序流程的控制。
作为scope guard,大多数情况下,我并不需要它保存状态,也不需要它可被寻址,那么它为什么就非得是个对象?很简单,在C++中,除了对象,没有其它有效的抽象机制能够完成这个任务。作为用户我无能为力。虽然仅对scope guard来说,用对象来实现没有决定性的不利(额外开销就算傻帽编译器愣着不给你优化也可能小到可以忽略不计),但也已经足够干扰到思维了——至少对于还没习惯这个技巧的人来说。
这种干扰很大一部分来自语义规则之间没有直观意义的耦合。另一方面,表述单一的行为时,需要依赖于很多规则,这些规则限制的程序行为的大部分只是为了让它们能够是well-formed,和表达者的本意无关。这应该是C++被认为复杂(多少是无谓的)的一个重要原因。
想一下,你写下这些例子的时候,为了提出~T() 的调用是编译or运行,用到多少东西?光是引用绑定规则+生存期就够人喝一壶的了。这只是沧海一粟。
……扯远了。
更本质上的,是析构函数的话题。
先回答你的问题:所有的~T()的调用是运行期。但在编译期(乃至之前)我能预料到这种调用出现的范围——在哪些调用之前,哪些调用之后。
很多情况下析构函数并非着重体现它最显著的(和“构造”相对的)意义:释放对象所有的资源,而仅仅是提供“对象生存期结束前发生的事件”的而已。这两者并非直接相关,只是由于C++对象模型的关系而相关。对象模型来源于C,但C的对象本质上只是存储,或者说只是数据,和控制流全然无关;显然进化成C++的现在这个需要做出不少修改。为什么在扩充对象模型的时候顺带附加了这些语义?是懒得去想应该多提供哪些接口么?把这两者复合在一起真的自然么?接口的简单却容易导致理解上的复杂。如果用户没有想到需要去理解对象模型,那么靠什么“学会”这种idiom,死记硬背还是故意去刷经验?
It works, and may be simple enough on writing. But what are the things we should comprehend when we see this portion of program text? Awesome, 1000 readers have their 1000 Hamlet, but nothing helps for programing directly, anyway. |
|