《C++0x漫谈》系列之:Concept, Concept!

本文探讨了C++0x(C++11)中引入的Concepts特性,旨在解决C++模板编程中遇到的诸多问题,如编译错误难以理解、模块式错误检查不足、IDE智能提示缺乏、文档与代码同步困难、重构不便、接口适配问题、SFINAE和模板技巧复杂性以及火星人类型系统的潜在风险。Concepts通过提升类型系统的抽象层次,实现更严格的模板约束,从而提供更清晰的错误信息、增强的IDE支持、更好的文档整合、简化重构和接口适配,以及更安全的类型系统。
摘要由CSDN通过智能技术生成

C++0x漫谈》系列之:Concept, Concept!

 

By 刘未鹏(pongba)

C++的罗浮宫(http://blog.csdn.net/pongba)

 

 

C++0x漫谈》系列导言

 

这个系列其实早就想写了,断断续续关注C++0x也大约有两年余了,其间看着各个重要proposals一路review过来:rvalue-referencesconceptsmemory-modelvariadic-templatestemplate-aliasesauto/decltypeGCinitializer-lists…

 

总的来说C++09C++98相比的变化是极其重大的。这个变化体现在三个方面,一个是形式上的变化,即在编码形式层面的支持,也就是对应我们所谓的编程范式(paradigm)C++09不会引入新的编程范式,但在对泛型编程(GP)这个范式的支持上会得到质的提高:conceptsvariadic-templatesauto/decltypetemplate-aliasesinitializer-lists皆属于这类特性。另一个是内在的变化,即并非代码组织表达方面的,memory-modelGC属于这一类。最后一个是既有形式又有内在的,r-value references属于这类。

 

这个系列如果能够写下去,会陆续将C++09的新特性介绍出来。鉴于已经有许多牛人写了很多很好的tutor这里这里,还有C++标准主页上的一些introductiveproposals,如这里,此外C++社群中老当益壮的Lawrence Crowl也在google做了非常漂亮的talk)。所以我就不作重复劳动了:),我会尽量从一个宏观的层面,如特性引入的动机,特性引入过程中经历的修改,特性本身的最具代表性的使用场景,特性对编程范式的影响等方面进行介绍。至于细节,大家可以见每篇介绍末尾的延伸阅读。

 

Concept

 

好吧好吧,我承认我跳票了,上次说这次要写variadic templates的。但g9老大写了一篇精彩的散记,让我觉得concept应该先写,因为这实在是个有意思的特性,比variadic templates有意思多了。

 

我和Concept不得不说的事

事儿#1

看看下面这坨代码有什么问题:

 

std::list<int> li;

std::sort(li.begin(), li.end());

 

如果对人肉编译不在行的话,可以用你手头的编译器试一下。你会发现,你的编译器一碰到这简单而无辜的两行代码便会一反常态,跟个长舌妇似的吐出一大堆&#$@*^,令人牙酸的错误信息来。在使用C++模板库时这种编译错误井喷是家常便饭,动辄喷出令人应接不暇的4K字节的错误信息出来。你还以为不是编译器井喷,而是你自己RP井喷了,于是一脸无辜地跑去问模板达人,后者抬了抬眼皮,告诉你说“把list改成vector因为listiterator不是random的而std::sort需要randomiterator”,你一边在脑子里给这句话分词加标点符号一边想弄明白他是怎么从一堆毛线似的字符里抽象出这么个结论的。

 

实际上,这个问题比你想像得严重,其根本问题在于降低工作效率,你得在你本不需要花工夫的地方(人肉解析编译错误)花工夫;这个问题比你想像得普遍,乃至于居然有人把“能够独立地解决所有的编译与链接问题”也列在了“有实际开发工作经验”要求里面;这个问题比你想像得影响恶劣,因为你可以想像可怜的新手在两行貌似无辜的代码面前哭丧脸的模样——C++编译器就这样把一个可怜的潜在C++用户给扼杀了。你也可以想像为什么有那么多人不喜欢C++模板——其实语法只是其一个非主要的方面。

 

实际上你请教的那个达人并没有什么火星抽象能力,只不过是吃过的桥比你走过的盐还多而已。而这,还预示着另一个问题,就是能人肉解析模板编译错误居然也成为了衡量C++达人与否的一个标准不信你去各个坛子上转一转看看有多少帖子是询问关于编译错误的问题的,其中又有多少是关于模板编译错误的。

 

更小概率的是居然还存在一个专门解析STL相关错误信息的“STL错误解码器”——STLFilt。这玩意帮你把编译错误转换成人能识别的自然语言,不错是不错。可惜STLFilt有了,BoostFilt呢?ACEFilt呢?我自己写的模板库呢?

 

其实,造成这个问题的直接原因是C++的类型系统的抽象层次太低。C++的静态强(也有人说C++的类型系统其实是弱类型系统,anyway)类型系统所处的抽象层面是在基本类型(intdoublechar…)层面的。一方面,C++虽然拥有对自定义类型的上乘支持(比如,支持将自定义类型的接口装扮得跟内建类型几乎毫无二致——vector vs. build-in array),然而另一方面,C++的类型系统却对于像vector这样的抽象从语意上毫不知情。直接的后果就是,一个高层的类型错误往往以相差了十万八千里的底层类型错误表现出来,结果就是你得充当一次福尔摩斯,从底层错误一直往上回溯最终找到问题的发生点。譬如一开始给出的那个例子:std::sort(li.begin(), li.end());的错误,如果C++类型系统的抽象层能高一些的话(所谓抽象层次高,就是知道高层抽象概念(Concept)的存在,如“随机迭代器”这个概念),给出的错误无非就是:“list的迭代器不满足随机迭代器这个概念(concept)的要求(requirements)”。然而由于C++并不知道所谓concept的存在,所以问题到它眼里就变成了“找不到匹配的operator+…”一堆nonsense

 

事儿#2

大二上学期的时候我们上一门计算方法的课程,期末考试要写一些矩阵算法。地球上的程序员大抵都知道矩阵算法不用Matlab算基本等于没事找抽,一大堆accidental complexities在那恭候着,一个index错误能让你debug到抓狂。当时我C++用得半斤八两,模板七窍也差不多通了六窍;为了到上机考试的时候节省点时间,就事先写了一个简单的矩阵库,封装了一些基本的操作和像高斯消元这种基本算法。

 

那个时候你能指望我知道TDD?还是XP?或者STLLint?于是呢?写了一个简单的程序,简单使用了一下写好的库,发现编译通过后就兴冲冲地告诉哥们说:大家不用怕,有我这Matrix库罩着,写算法跟写伪码差不到哪去!

 

两天后上机考试,程序不同了,等于测试用例不同了,结果原来没有出现的编译错误一下统统跑出来了。原来为什么不出现?一个原因是原来有些成员函数就没用到,C++说,在一个模板类里面,没用到的成员函数是不予编译的。那不予编译就代表不予纠错吗?不予类型检查吗?令人悲伤的是,的确如此。或者把置信度提高一点说,几乎如此。为什么?看看下面的代码:

 

template<typename T>

void f(T& t)

{ <

  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 91
    评论
评论 91
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值