当前位置: 首页 > 图文教程 > 开发语言 > VC++ > What are you, Anyway?

VC++
如何在运行时确定对象类型(RTTI)
FMD开发文集 -- CArchive原理
FMD开发文集 -- MFC调试模式下new操作符的特殊处理
FMD开发文集 -- MFC CObject浅析
生死疆界(下)--- 在new与delete之间
生死疆界(上)--- 在new与delete之间
Pointers 与 References(四)
Pointers 与 References(三)
Pointers 与 References(二)
Pointers 与 References(一)
介绍一个专门处理C++异常的类和例子
关于构造单实例类的一个问题
模板友元化
C++中类的数据成员的安全隐患
关于对象生命历程的会话
接触VC之二:MFC类基础,C++程序编写规范介绍
内联汇编基础知识
命名空间的概念
捕获数学函数异常
MMX指令集在C++中的使用

VC++ 中的 What are you, Anyway?


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-10-30   浏览: 36 ::
收藏到网摘: n/a

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 获得实例化的模板信息。

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的情况:
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