以下代码给出了编译错误'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()
的范围内没有声明'价值'是什么?
答案 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::B
,A::a
和A::Y
不会影响名称的绑定A
。 - 结束示例]