43

考虑:

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

4 回答 4

48

这对您来说是两阶段查找。

Base<T>::NO_ZEROFILL(所有大写标识符都是 boo,除了宏,顺便说一句)是一个依赖于T.
因为,当编译器第一次解析模板时,还没有实际的类型被替换T,编译器不“知道”什么Base<T>是。因此它无法知道您假设在其中定义的任何标识符(T编译器稍后才看到的某些 s 可能有专门化),并且您不能从基类中定义的标识符中省略基类限定。

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

于 2009-08-06T16:12:16.243 回答
29

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

在查找模板定义中使用的名称声明时,通常的查找规则(3.4.1、3.4.2)用于非依赖名称。依赖于模板参数的名称查找被推迟到知道实际的模板参数(14.6.2)。

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

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

这种正常的查找不会在内部进行搜索Base<T>,因此 NO_ZEROFILL 只是一个未声明的标识符。14.6.2/3 有:

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

当您实质上符合条件NO_ZEROFILLBase<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并查看有一个调用,bar该调用具有一个依赖参数(即依赖于模板参数的参数)。此时编译器根据 3.4.1 查找 bar,这是“阶段 1 查找”。查找将找到该函数void bar (int),并将其与相关调用一起存储,直到稍后。

当模板被实例化时(作为调用 from 的结果main),编译器然后在参数范围内执行额外的查找,这就是“阶段 2 查找”。这种情况导致发现void NS::bar(A const &).

编译器有两个重载 forbar并在它们之间进行选择,在上述情况下调用void NS::bar(A const &).

于 2009-08-10T10:16:23.870 回答
1

似乎在 vs 2008 中可以编译。您是否尝试过:

public:
    Derived( bool initZero = Base<T>::NO_ZEROFILL );
于 2009-08-06T16:11:03.950 回答
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;}
于 2015-04-14T03:50:16.810 回答