基类模板的成员超出了具有相同模板参数的派生类模板的范围

时间:2013-04-04 14:19:37

标签: c++ templates

以下代码给出了编译错误'value'未在此范围内声明

template<class T>
struct Base {
    int value;
};

template <class T>
struct Derived : public Base<T> {
    int getValue() { return value; }
};

我觉得非常奇怪

  • 如果Derived继承自Base<std::string>,则代码会编译,
  • 如果我return Base<T>::value,代码会编译。

为什么代码不能按原样编译?在Derived<T>::getValue()的范围内没有声明'价值'是什么?

1 个答案:

答案 0 :(得分:20)

因为value非限定名称,并且在名称查找的第一阶段,编译器将不知道这是从基类继承的数据成员(它没有& #39; t实例化Base<T>。因此,它将搜索全局命名空间,并找不到名为value的变量;因此,它会发出错误。

以下是解决此问题的典型方法:

template <class T>
struct Derived : public Base<T> {
    int getValue() { return this->value; }
    //                      ^^^^^^
};

显式解除引用this告诉编译器后面的名称是(可能是继承的)数据成员的名称,并且查找应该被延迟到实际实例化成员函数的位置。当然,你做的解决方案:

return Base<T>::value;

同样好,因为它还告诉编译器value是从基类Base<T>继承的。

对于从Base<std::string>派生的问题,编译器可以立即查找Base<std::string>是否包含名为value的数据成员(因为它不依赖于任何模板参数)和如果是这种情况,它将能够确定表达式是否格式正确。

但是,如果您的基类是Base<T>,其中T在名称查找的第一阶段是未知的,则编译器无法告诉value是什么(专业化不同Base的{​​{1}}甚至可能根本没有T

C ++ 11标准的第14.6 / 3段:

  

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

value
     

模板参数struct A { struct B { / ... / }; int a; int Y; }; int a; template<class T> struct Y : T { struct B { / ... / }; B b; // The B defined in Y void f(int i) { a = i; } // ::a Y* p; // Y<T> }; Y<A> ya; 的成员A::BA::aA::Y不会影响名称的绑定   A。 - 结束示例]