C ++:模板化基类的范围?

时间:2015-09-17 01:45:58

标签: c++ templates inheritance scope

此代码无法编译:

template <typename T>
struct B {
    typedef T type;
};

struct D0 : public B<int> {
    void h(type) { }
};

template <typename T>
struct D : public B<T> {
    void f(typename B<T>::type) { }
    void g(type) { }
};

具体来说,尽管编译D0::h(type)D::f(typename B<T>::type),但D::g(type)却没有。为什么type中无法看到D

2 个答案:

答案 0 :(得分:4)

答案可以在两个地方找到。第一个C ++ 11标准草案N3337

§14.6.2/ 3

  

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

typedef double A;
template<class T> class B {
  typedef int A;
};
template<class T> struct X : B<T> {
  A a;              // a has type double
};
     

A定义中的类型名称X<T>绑定到typedef   在全局命名空间范围中定义的名称,而不是typedef名称   在基类B<T>中定义。 - 结束示例]

(大胆强调我的)

其次,parashift FAQ在Why am I getting errors when my template-derived-class uses a nested type it inherits from its template-base-class?中提供了一个人类可读的解释:

  

也许令人惊讶的是,以下代码无效C ++,尽管如此   一些编译器接受它:

template<typename T>
class B {
public:
  class Xyz { /*...*/ };  // Type nested in class B<T>
  typedef int Pqr;        // Type nested in class B<T>
};
template<typename T>
class D : public B<T> {
public:
  void g()
  {
    Xyz x;  // Bad (even though some compilers erroneously (temporarily?) accept it)
    Pqr y;  // Bad (even though some compilers erroneously (temporarily?) accept it)
  }
};
     

这可能会伤害你的头脑;如果你坐下来会更好。

     

D<T>::g()中,名称XyzPqr不依赖于模板参数   T,因此它们被称为非独立名称。另一方面,B<T>   取决于模板参数T,因此B<T>称为依赖项   名。

     

这是规则:编译器不查看依赖的基类   (例如B<T>)查找非依赖名称(例如XyzPqr)。作为一个   结果,编译器不知道它们甚至存在,更不用说了   类型。

     

此时,程序员有时会在其前面添加B<T>::,例如:

template<typename T>
class D : public B<T> {
public:
  void g()
  {
    B<T>::Xyz x;  // Bad (even though some compilers erroneously (temporarily?) accept it)
    B<T>::Pqr y;  // Bad (even though some compilers erroneously (temporarily?) accept it)
  }
};
     

不幸的是,这也不起作用,因为那些名字(是你   准备?你坐下了吗?)不一定是类型。 “嗯?!?”你   说。 “不是类型?!?”你惊叹道。 “太疯狂了;任何傻瓜都可以看到他们   是类型;看看!!!“你抗议。对不起,事实是他们   可能不是类型。原因是可以有专业化   例如B<T>B<Foo>,其中B<Foo>::Xyz是数据成员。   由于这种潜在的专业化,编译器无法假设   B<T>::Xyz是一种类型,直到它知道T。解决方案是给予   通过typename关键字编译提示:

template<typename T>
class D : public B<T> {
public:
  void g()
  {
    typename B<T>::Xyz x;  // Good
    typename B<T>::Pqr y;  // Good
  }
};

答案 1 :(得分:1)

显然,这是由于编译器的语义编译阶段的顺序。碰巧的是,对于编译器的视图,符号type不存在于任何范围内,因为它还没有(尚未)实例化模板,因此它本身没有实例化D。 / p>

唯一的解决方法是强制编译器在语义编译的后期阶段解析符号,例如使用...

void f(typename B<T>::type) {}

或者,如果派生类是模板,但基类是,则编译器强制首先解析/实例化/基类,因此避免在派生类中出现任何可能的错误。

但是,如果 base和derived都是模板,则不是这种情况,因为编译器将在所有已经实例化的范围(例如全局范围)中查找派生符号 实例化派生模板类之前,如果它失败了,你已经知道int main(){printf("%d\n", argc);}会发生什么,不是吗?