|
在经过艰难的讨论template metaprogramming很长时间后,返回到我们学习的开始。 在这一部分,我们来了解模板编程的更为模糊的语法问题:在编译器没有充分的信息的情况下,怎样引导编译器进行分析。在这里,我们将讨论标准容器中用来消除歧义的“rebind”机制。同时,我们也将对一些潜在的模板编程技术进行热烈的讨论。 甚至经验丰富的C++程序员,也常常被模板的复杂的语法所困扰。在所以模板语法中,我们首先要了解:消除编译器分析的歧义,是最基本的语法困惑。 Types of Names, Names of Types 让我们看看一个没有实现标准容器的模板例子,这个例子很简单。 template <typename T> class PtrList { public: //... typedef T *ElemT; void insert( ElemT ); private: //... }; 常常模板类嵌入Type names信息,这样,我们就可以通过正确的nested name 获得实例化的模板信息。 typedef PtrList<State> StateList; //... StateList::ElemT currentState = 0; 嵌入类型的ElenT允许我们可以,很容易的访问PtrList模板的所承认的元素类型。 即使我们用State类型初始化PtrList,元素类型还将是State*。在其他一些情况下,PtrList 可以用指针元素实现。一个比较成熟的PtrList的实现,应该是可以随着初始化的元素类型而变化的。使用nested type,可以帮助我们封装PtrList,以免用户了解内部的实现。 下面还有一个例子: template <typename Etype> class SCollection { public: //... typedef Etype ElemT; void insert( const Etype & ); private: //... }; SCollection的实现跟PtrList一样,遵守标准命名的条款。遵守这些条款是有用的,这样我们就可以写出很多优雅的算法来使用这些容器(译注:像标准模板库一样)。例如:可以写一个如下的算法:用适当的元素类型来填充这个容器数组。 template <class Cont> void fill( Cont &c, Cont::ElemT a[], int len ) { // error! for( int i = 0; i < len; ++i ) c.insert( a[i] ); } 蹩脚的编译器 很遗憾的是,在这里我们有一个语法错误。编译器不能识别Cont::ElemT这个type name。问题是在fill()的上下文中,没有足够的信息让编译器知道ElemT是一个type name。在标准中规定,在这种情况下,认为nested name 不是type name。 现在刚刚开始,如果你没有理解,不要紧。我们来看看在不同的上下文中,编译器所获得的信息。首先,让我们来看看在没有模板的class的情况:
|