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

VC++
在类VC的界面实现中加入目录树
软件换肤技术在 BCB 中的实现
利用非模窗口生成MDI介面
报表输出轻松搞定
Windows 中不规则窗体的编程实现
解说Win32的窗口子类化
使用测试优先方法开发用户界面
一个简单的登录对话框的实现
一个简单的日记本程序
从资源中加载皮肤
一个在RichEdit中添加表情图象的类
ActiveSkin 4.3 软件换肤在VC中的实现
一种另类“关于(About)”对话框的动态显示方法
对话框打印预览及打印
关于如何换肤、子类化的解决方案
制作 MSN、QQ 的消息提示窗口
如何对 BCGControlBarPro 进行换肤
定制个性化的对话框窗口类
改变窗口中的光标形状
更新MFC中的视图,跟踪.NET Framework中的事件

VC++ 中的 What are you, Anyway?


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-10-30   浏览: 45 ::
收藏到网摘: 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