What are you, Anyway?,What are you, Anyway?
【 tulaoshi.com - C语言心得技巧 】
What are you, Anyway?
作者:Stephen C. Dewhurst 译者:陶章志
原文出处:http://www.cuj.com/documents/s=8464/cuj0308dewhurst/
在经过艰难的讨论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 获得实例化的模板信息。
(本文来源于图老师网站,更多请访问http://www.tulaoshi.com)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] );
}
蹩脚的编译器
(本文来源于图老师网站,更多请访问http://www.tulaoshi.com)很遗憾的是,在这里我们有一个语法错误。编译器不能识别Cont::ElemT这个type name。问题是在fill()的上下文中,没有足够的信息让编译器知道ElemT是一个type name。在标准中规定,在这种情况下,认为nested name 不是type name。 现在刚刚开始,如果你没有理解,不要紧。我们来看看在不同的上下文中,编译器所获得的信息。首先,让我们来看看在没有模板的class的情况:
class MyContainer {
public:
typedef State ElemT;
//...
};
//...
MyContainer::ElemT *anElemPtr = 0;
由于编译器可以检测到MyContainer class的上下文确定有个ElemT的成员类型,从而可以确认MyContainer::ElemT确实是一个type name。在实例化的模板类中,其实,也跟这种情况一样简单。
typedef PtrList<State StateList;
//...
StateList::ElemT aState = 0;
PtrList<State::ElemT anotherState = 0;
对于编译器来说,一个实例化的模板类跟一个普通的类一样。在存储PtrList<State的nested name 和在MyContainer中是一样的,没有什么差别。在其他情况下,编译器也是这样检查上下文来看ElemT是不是type name。然而,当我们进入template的上下文后,事情就变得复杂了。因为在这,没有充分的准确信息。考虑下面的程序片断:
template <typename T
void aFuncTemplate( T &arg ) {
...T::ElemT...
当编译器遇到T::ElemT,它不知道这是什么。从模板的申明中,编译器知道,T是一个类型名。它通过::运算符也能猜测出T是一个类型名。但是,这就是所有编译器知道的。因为,这里没有关于T的更多的信息。例如:我们能够用PtrList来调用一个模板函数,在这里,T::ElemT将是一个Type name。
PtrList<State states;
//...
aFuncTemplate( states ); // T::ElemT is PtrList<State::ElemT
But suppose we were to instantiate aFuncTemplate with a different type?
struct X {
double ElemT;
//...
};
X anX;
//...
aFuncTemplate( anX ); // T::ElemT is X::ElemT
在这个例子中,T::ElemT是数据类型,不是type name。编译器将怎么办呢?在标准中规定,在这种情况下,编译器将认为nested name 不是type name。在将在上述fill()模板函数中导致一个语法错误。
Clue In the Compile
来源:http://www.tulaoshi.com/n/20160129/1485817.html
看过《What are you, Anyway?》的人还看了以下文章 更多>>