为什么派生的模板类不能访问基本模板类的标识符?
考虑:
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)编译这个。
在将它们转换为类模板之前,它们是非generics的,派生类能够看到基类的静态成员。 在C ++规范的要求中是否会丢失可见性,或者是否需要使用语法更改?
我知道Base<T>
每个实例都有它自己的静态成员“ NO_ZEROFILL
”和“ NO_ZEROFILL
”, Base<float>::ZEROFILL
和Base<double>::ZEROFILL
是不同的variables,但我并不真正关心; 常量是代码的可读性。 我想使用一个静态常量,因为名称冲突更安全,而不是macros观或全局。
这是你的两阶段查找。
Base<T>::NO_ZEROFILL
(所有标识符都是boo,除了macros,BTW)是一个依赖于T
的标识符。
因为当编译器首先parsing模板时,没有实际的typesreplaceT
,编译器不知道Base<T>
是什么。 所以它不可能知道任何你想要在其中定义的标识符(对于某些编译器稍后会看到的T
也许是专门化的),并且不能从基类中定义的标识符中省略基类限定。
这就是为什么你必须编写Base<T>::NO_ZEROFILL
(或this->NO_ZEROFILL
)。 这就告诉编译器, NO_ZEROFILL
是基类中的东西,它依赖于T
,而且它只能在模板实例化的时候才能进行validation。 因此,它会接受它而不尝试validation代码。
该代码只能在稍后通过提供T
的实际参数来实例化模板时才能被validation。
您遇到的问题是由于从属基类的名称查找规则。 14.6 / 8有:
在查找模板定义中使用的名称声明时,通常的查找规则(3.4.1,3.4.2)用于非依赖名称。 推迟依赖于模板参数的名称查找(14.6.2)直到实际的模板参数已知(14.6.2)。
(这不是真正的“两阶段查找” – 请参阅下面的解释。)
关于14.6 / 8的一点是,就编译器而言,在你的例子中NO_ZEROFILL
是一个标识符,并且不依赖于模板参数。 因此,按照3.4.1和3.4.2的正常规则查找。
这个正常的查找不会在Base<T>
内search,所以NO_ZEROFILL只是一个未声明的标识符。 14.6.2 / 3有:
在类模板或类模板成员的定义中,如果类模板的基类依赖于模板参数,则在类的定义点处,在非限定名称查找期间不会检查基类作用域模板或成员或在类模板或成员的实例化过程中。
当你使用Base<T>::
来NO_ZEROFILL
时,实质上你正在将它从一个非依赖名称改为一个依赖名称,当你这样做的时候,你延迟了它的查找,直到模板被实例化。
附注:什么是两阶段查找:
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); }
上面的例子编译如下。 编译器parsingfoo
的函数体,看看是否有一个bar
的调用,它有一个依赖参数(即依赖于模板参数的参数)。 在这一点上,编译器按照3.4.1查找栏,这是“阶段1查找”。 查找将find函数void bar (int)
并将其与依赖性调用一起存储,直到稍后。
当模板被实例化时(作为main
调用的结果),编译器会在参数范围内执行一个额外的查找,这就是“阶段2查找”。 这种情况导致findvoid NS::bar(A const &)
。
编译器有两个重载bar
,它在它们之间进行select,在上面的例子中调用void NS::bar(A const &)
。
似乎在VS 2008编译好。你尝试过:
public: Derived( bool initZero = Base<T>::NO_ZEROFILL );
试试这个程序
#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;}
行u也可以使用范围parsing(::)运算符。 例如,尝试更换行
T get(void){return base<T>::x+2;}