为什么派生的模板类不能访问基本模板类的标识符?
考虑:
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>::ZEROFILL
和Base<double>::ZEROFILL
是不同的变量,但我并不在乎; 该常量用于代码的可读性。 我想使用静态常量,因为就名称冲突而言,这比使用宏或全局方法更安全。
这是您的两阶段查找。
Base<T>::NO_ZEROFILL
(除大写字母BTW外,所有大写字母标识符均为boo)是取决于this->NO_ZEROFILL
的标识符。
由于当编译器首次解析模板时,尚无实际类型替代NO_ZEROFILL
,因此编译器无法“知道” T
是什么。 因此,它无法知道您假定要在其中定义的任何标识符(某些3007655734604608921604s可能会有专门化的含义,编译器稍后会看到),并且您无法从基类中定义的标识符中忽略基类限定条件。
这就是为什么您必须写Base<T>::NO_ZEROFILL
(或this->NO_ZEROFILL
)的原因。 这告诉编译器NO_ZEROFILL
是基类中的内容,它取决于T
,并且只能在实例化模板时稍后进行验证。 因此,它将接受它而不尝试验证代码。
当通过提供3007655734608928921604的实际参数实例化模板时,只能稍后验证该代码。
您遇到的问题是由于相关基类的名称查找规则所致。 14.6 / 8具有:
查找模板定义中使用的名称声明时,通常使用查找规则(3.4.1, 3.4.2)用于非依赖性名称。 取决于模板参数的名称查找为 推迟到知道实际的模板参数(14.6.2)。
(这并不是真正的“两阶段查找”-有关此内容的说明,请参见下文。)
关于14.6 / 8的一点是,就编译器而言,在您的示例中bar
是标识符,并且不依赖于模板参数。 因此,按照3.4.1和3.4.2中的常规规则进行查找。
此常规查询不会在bar
内部进行搜索,因此NO_ZEROFILL只是一个未声明的标识符。 14.6.2 / 3具有:
在类模板或类模板的成员的定义中,如果类模板的基类 取决于模板参数,在不限定名称查找期间不检查基类范围 在类模板或成员的定义时或在类模板的实例化期间 或成员。
本质上,当您将bar
与void NS::bar(A const &)
进行限定时,您会将其从一个非依赖名称更改为一个依赖名称,并且在这样做时,将延迟其查找,直到实例化该模板。
旁注:什么是两阶段查找:
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);
}
上面的示例编译如下。 编译器解析bar
的函数主体,然后看到对具有依赖参数(即,依赖于模板参数的参数)的void NS::bar(A const &)
的调用。 此时,编译器按照3.4.1进行查找,这就是“阶段1查找”。 查找将找到函数void bar (int)
,该函数与从属调用一起存储,直到以后。
然后实例化模板时(作为bar
的调用的结果),编译器然后在参数范围内执行附加查找,这就是“阶段2查找”。 这种情况导致找到void NS::bar(A const &)
。
编译器对bar
有两个重载,并在它们之间进行选择,在上述情况下,调用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;
}
在3007657878787457458048行中,您还可以使用范围解析(::)运算符。 例如,尝试用
T get(void){return base<T>::x+2;}