考虑:
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
不同变量,但我真的不在乎;常量是为了代码的可读性。我想使用静态常量,因为就名称冲突而言,它更安全,而不是宏或全局。
答案 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;}