为什么派生的模板类不能访问基本模板类的标识符?

时间:2009-08-06 16:06:07

标签: c++ templates derived-class c++-faq

考虑:

template <typename T>
class Base
{
    public:
        static const bool ZEROFILL = true;
        static const bool NO_ZEROFILL = false;
}

template <typename T>
class Derived : public Base<T>
{
    public: 
        Derived( bool initZero = NO_ZEROFILL );    // NO_ZEROFILL is not visible
        ~Derived();
}

我无法用GCC g ++ 3.4.4(cygwin)编译它。

在将这些转换为类模板之前,它们是非泛型的,派生类能够看到基类的静态成员。在C ++规范的要求中是否会失去可见性,还是需要使用语法更改?

我了解Base<T>的每个实例都会拥有自己的静态成员“ZEROFILL”和“NO_ZEROFILL”,Base<float>::ZEROFILLBase<double>::ZEROFILL不同变量,但我真的不在乎;常量是为了代码的可读性。我想使用静态常量,因为就名称冲突而言,它更安全,而不是宏或全局。

4 个答案:

答案 0 :(得分:46)

这是你的两阶段查找。

Base<T>::NO_ZEROFILL(所有大写标识符都是boo,除了宏,BTW)是一个取决于T的标识符。
因为,当编译器首先解析模板时,还没有替换T的实际类型,编译器不会“知道”Base<T>是什么。因此,它无法知道您假定在其中定义的任何标识符(编译器稍后可能会看到某些T的特化),并且您不能从基类中定义的标识符中省略基类限定。

这就是你必须写Base<T>::NO_ZEROFILL(或this->NO_ZEROFILL)的原因。这告诉编译器NO_ZEROFILL是基类中的某个东西,它依赖于T,并且它只能在以后实例化模板时验证它。因此,它会在不试图验证代码的情况下接受它 只有在通过提供T的实际参数来实例化模板时,才能在以后验证该代码。

答案 1 :(得分:29)

您遇到的问题是由于依赖基类的名称查找规则。 14.6 / 8有:

  

在查找模板定义中使用的名称声明时,通常的查找规则(3.4.1,   3.4.2)用于非独立名称。根据模板参数查找名称是   推迟到实际模板参数已知(14.6.2)。

(这不是真正的“两阶段查找” - 请参阅下面的解释。)

关于14.6 / 8的观点是,就编译器而言,{1}在您的示例中是一个标识符,并且不依赖于模板参数。因此,它按照3.4.1和3.4.2中的正常规则进行查找。

此正常查找不会在NO_ZEROFILL内搜索,因此NO_ZEROFILL只是一个未声明的标识符。 14.6.2 / 3有:

  

在类模板的定义或类模板的成员中,如果是类模板的基类   取决于模板参数,在非限定名称查找期间不检查基类范围   在类模板或成员的定义点或在类模板的实例化期间   或会员。

当您使Base<T> NO_ZEROFILL从本质上限定时,您正在将其从非依赖名称更改为从属名称,当您这样做时,您会延迟查找,直到模板被实例化为止。

旁注:什么是两阶段查找:

Base<T>::

以上示例编译如下。编译器解析void bar (int); template <typename T> void foo (T const & t) { bar (t); } namespace NS { struct A {}; void bar (A const &); } int main () { NS::A a; foo (a); } 的函数体,并看到有foo的调用,它有一个依赖参数(即一个依赖于模板参数的参数)。此时,编译器按3.4.1查找栏,这是“阶段1查找”。查找将找到函数bar,并与依赖调用一起存储,直到稍后。

然后实例化模板(作为来自void bar (int)的调用的结果),然后编译器在参数的范围内执行额外的查找,这是“阶段2查找”。这种情况导致找到main

编译器有两个void NS::bar(A const &)重载,它在它们之间进行选择,在上面的例子中调用bar

答案 2 :(得分:1)

似乎在vs 2008中编译好了。你试过了吗?

public:
    Derived( bool initZero = Base<T>::NO_ZEROFILL );

答案 3 :(得分:0)

试试这个程序

#include<iostream>
using namespace std;
template <class T> class base{
public:
T x;
base(T a){x=a;}
virtual T get(void){return x;}
};
template <class T>
class derived:public base<T>{
public:
derived(T a):base<T>(a){}
T get(void){return this->x+2;}
};
int main(void){
base<int> ob1(10);
cout<<ob1.get()<<endl;
derived<float> ob(10);
cout<<ob.get();
return 0;
}
T get(void){return this->x+2;}行中的

你也可以使用范围解析(::)运算符。例如,尝试用

替换该行
T get(void){return base<T>::x+2;}