C++模板编程(16)---继承与类模板(Derivation and Class Templates)

Class Templates 可以继承其他classes,也可以被其他classes继承。大多数情况下 class templates 和 non-templates classes 在这方面并没有什么重大差别。但是有一个微妙和重要的问题,出现在“由一个受控名称dependent name 所指涉的base class,衍生出一个class template”时,我们先从简单的non-dependent base classes说起。

1. 非受控的Non-dependent Base Classes

在class template中, 所谓non-template base classes 是指一个无需知道任何template argument就可以确定地类型。换句话说,用来指涉该base class的名称,是个非受控名称non-dependent name。例如:

template <typename X>

class Base{

public:

        int basefield;

        typedef int T;

};

class D1:public Base<Base<void> > {   //并不真正是个template

public:

        void f() {basefield = 3; }      // 以通常方法继承来的成员

};

template <typename T>

class D2: public Base<double> { // non-dependent base class

public:

        void f() {basefield = 7;}   // 以一般方法存取继承而来的成员

        T strange;                    //T是Base<double>::T, 不是模板参数template parameters

};

non-dependent base templates的行为和常规的non-template base classes 非常相似,但是有个令人惊异的差异是:当编译器在derived class template中查询一个未受饰名称unqualified name时,会优先查询non-dependent base templates,然后才查询template parameters.这意味着上面例子中,class template D2的strange将拥有Base<double>::T类型(本例为int)。

因此以下函数语句非法:

void g(D2<int*> & d2, int* p)

{

        d2.strange = p; // 错误,类型不匹配:trange是int, p 是int*

}

这与直觉相悖(感觉int*,就是模板实参传给了T),derived template 编写者因此特别留心其non-dependent bases中的名称,甚至是简介继承,或名称是private。较好的做法是把template parameters 放在被它们模板化的物体的作用域内。

2. 受控的Dependent base Classes

上面的例子中,base class是完全确定的。它不取决于某个template parameter。也就是说C++

编译器只要是见到template的定义,就可以在base classes中查询非受控名称。C++标准规定,编译器只要见到一个非受控名称non-dependent name,就立即开始查询lookup。

考虑下面的例子:

template <typename T>

class DD: public Base<T> {    //dependent base

public:

        void f() {basefield = 0;}    //(1) have some problem...

};

template <>

class Base<bool> {                    //explicit specialization

        enum { basefield = 42 };    // (2) a tricky

};

void g(DD<bool>& d)

{

        d.f();                               //(3) oh,oh?

}

在(1)处我们发现,这里使用了一个非受控名称basefield:编译器一定会立即查询它。假设我们在template base中查询它,并将它系结为一个(在基类中找到的)int成员。然而之后,又立刻在(2)对这个泛型定义进行了特化,将basefield改成我们看到的枚举变量。于是当(3)处实例化DD:f的定义时,会发现(1)处对basefield的类型解释并不准确。

(2)处特化的DD<bool> 之中并没有可变化的basefield,因此编译器会报错。

为了解决这个问题,C++标准规定非受控名称non-dependent names不在受控的base class中查询。因此符合标准的C++编译器会在(1)处报错。欲修正上述程序代码,我们可以令basefield成为一个受控名称dependent name,而受控名称的查询过程会发生在它被实例化之后。此时basefield的特化类型就可以被编译器发现。例如,在(3)处,编译器会知道DD<bool>的base class是Base<bool>,而Base<bool>已被实例化。据此,我们可以修改程序代码如下:

//method 1

template <typename T>

class DD1: public Base<T> {

public:

        void f() { this->basefield = 0;} //delay the lookup

};

另一种做法是使用一个受饰名称qualified name导入相依性(受控性,dependency)

//method2

template <typename T>

class DD2:public Base<T> {

public:

        void f() { Base<T>::basefield = 0; }

};

如果觉得这么多修饰符号会弄乱程序代码,可用using声明语句将某个名称从受控的base class中带到当前作用域中,于是你就可以肆意使用它。

 //method 1

template <typename T>

class DD3: public Base<T> {

public:

        using Base<T>:: basefield; //(1) 现在,basefiled在当前域中为受控名称

        void f() { basefield =0; }  //(2)正确

};

(2)进行的查询过程就会成功,它会找到(1)处的声明语句。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值