关注C++0x: Concept

  要说C++0x中对泛型编辑最大的改变,当属Concept了。
Concept这个东西,其实并不是刚刚提出来的。可以说,Concept是STL的基础。

STL之于C++的地位不言而寓,而其中最重要的,当属迭代器(Iterator)的概念(Concept)了。这是接触STL后得到的最明显的Concept。有了迭代器,就可以将算法与容易分离开来,这样就奠定了STL的基础。于是一个又一个服务于STL的概念(Concept)就诞生了。
不过相信大多数人只是直接使用STL,因此对于Concept没有什么印象。一旦涉及到自行创建程序库,特别是创建相容于STL规范的容器或算法的时候,就肯定要接触到STL中的一堆Concept了。
举个简单的例子,如何实现一个Iterator?STL中的Iterator有几个类别:ForwardIterator、BidirectionalIterator、RandomAccessIterator等5类。现在需要实现一个用于vector容器的Iterator,显然是要符合上述三种类型。但是要从STL找到象ForwardIterator的通用类,肯定是找不到。最多只能找到一个Iterator的通用定义:
template<class Category, class Type, class Distance = ptrdiff_t
   
class Pointer = Type*class Reference = Type&>
   
struct iterator {
   typedef Category iterator_category;
   typedef Type value_type;
   typedef Distance difference_type;
   typedef Pointer pointer;
   typedef Reference reference;
   };
很昏倒吧,全是typedef。至于更具体的Concept,只好打开C++的标准文档了,想要满足ForwardIterator要哪些需求,满足RandomAccessIterator要哪些需求,然后对照着去实现。
至于实现后的Iterator是否真的符合这些Concept,就只好摸摸自已的良心,问问是否完全测试过了。
为什么会这样呢?因为这些Concept目前只停留在文档中。C++编译器对Concep完全没有概念。更过份的是,C++编译器甚至完全看不到或只看到一部份刚才定义的Iterator--因为大多数编译器仅对确实需要具现化的类或成员才进行编译。

比如:
template<typename T>
class test
{
public:
    
void kill_cpp(T t)
    {
        不要调用我啊!
    }
};
int main(int argc, char* argv[])
{
    test
<int> t;
    
//t.kill_cpp(0);
    return 0;
}
编译就完全通过,C++编译器对“不要调用我啊!”这几个字视而不见。我有时候就利用这个特点,以保证某些函数确实不应该被调用,而一旦被误调用了,还可以有一个能看得明白的错误信息:
error C2065: '不要调用我啊!': undeclared identifier  (VC2008 Beta2)

也许STL的Concept还比较少接触。那么在写泛型程序时,也总会遇到一些类似的情形。比如下面的代码:
template<typename T>
static void Show(const T& t)
{
    cout 
<< t.Message() << endl;
}
这个函数用于显示某个类的信息。显然,它要求该类型必须拥有 Message 成员函数。但这个函数的用户知道吗?也许知道,因为你文档写的很详细。可是一旦用户忘记或者误用了,那麻烦就出来了,肯定是一堆错误信息。这个例子还比较简单,也就多几个错误信息,稍微仔细查看,还是能够解决问题。那么下面的例子:
#include <list>
#include 
<algorithm>
using namespace std;

int main(int argc, char* argv[])
{
    list
<int> lst;
    sort(lst.begin(), lst.end());
    
return 0;
}
这个例子将会产生一大堆的错误信息,足以让刚接触到的人如坠云雾。而实际原因只不过是list的iteraotr不是RandomAccessIterator(也就是说list不能象数组一样随机访问,所以不能使用sort函数)。

Java和.NET等新加入泛型编程特性的语言,对此自然深有体会,不约而同都加入了模板约束的机制。假如让.NET来实现这个sort的话,它可能会这么写:
void sort<T>(T container) where T : IEnumerable
{
 
}
这样,当container被代入共它类型,如int时,编译时将会给出明确的错误:
The type 'int' cannot be used as type parameter 'T' in the generic type or method sort<T>(T)'.
There 
is no boxing conversion from 'int' to 'System.Collections.IEnumerable'. (VC# 2008 Beta2)

是啊,模板约束很有用,至少可以让我们放心,编译器会帮我们检查那个T要满足什么条件。于是开始羡慕起Java和C#了:毕竟是后来者,把C++的优点采用了,缺点补上了...
为了让C++也能拥有这个特性,C++的模板库编写者真的是费尽心思。类似模板元编程等技术横空出世。boost有相当部份的程序类在为此努力。但是所有的努力,都仅仅只能让程序库的易用性有一些改善,却大大增加了程序库的编写复杂度和可维护性。势必在C++语言中内置相关的特性,才是最终的解决方案。

盼啊盼啊,终于盼来了C++0x,而且令人惊喜的是,C++并没有采用Java和C#那一套模板约束的方式,而是将STL的Concept从文档化变成语言特性了。这个变化我认为是革命性的,而且还带来了前所未有的新的编程方式,将创建继模板以来的新的流行--可以预见,C#和Java最终也将会采用这个特性。
Concept的应用包含了C#和Java中的模板约束,但不止于此。模板约束仅仅是基本。

对于上面sort的例子,在C++0x中将会出现比较简洁的错误信息:
sort.cpp: In function 'void f()':
sort.cpp:
7: error: no matching function for call to 'sort(std::_List_iterator<int>, std::_List_iterator<int>)'
<path>: note: candidates are: void std::sort(_Iter, _Iter) [with _Iter = std::_List_iterator<int><where clause>
sort.cpp:
7: note:   no concept map for requirement 'std::MutableRandomAccessIterator<std::_List_iterator<int> >'
错误信息一条:找不到可用的sort函数。
提示信息一条:List的Iterator没有到MutableRandomAccessIterator的映射。
稍微动一下脑,也就明白sort是需要随机访问的Iterator,而List既没有该类别的Iterator,也没有可以到该类别的Iterator的映射。
解决方法有两个:一是换用有随机访问的Iterator的容器,如vector;一是实现一个到MutableRandomAccessIterator的映射。

Concept目前在C++0x的提案已经确认肯定会被通过了。Concept引入C++,目的是要让模板库得容易使用,和容易编写。

对Concept有兴趣的朋友可以查看相关的文档。也许因为C++0x众多令人激动的特性,我会用我的方式进行一一表达。

相关链接:
http://del.icio.us/pongbablog/cplusplus
  《C++0x漫谈》系列之:Concept, Concept!
  C++0x展望[语言核心进化]
呵呵,偷一下懒,刘未鹏的文章推荐对C++0x感兴趣的朋友观看,文后所附的资料及相关的标准提案相当详细。这里就不照抄了。

posted @ 2007-09-09 22:43 沐枫 阅读(...) 评论(...) 编辑 收藏