要说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感兴趣的朋友观看,文后所附的资料及相关的标准提案相当详细。这里就不照抄了。