C++ 为什么我必须通过this指针访问模板基类成员?
如果下面的类不是模板,我可以在C++ 为什么我必须通过this指针访问模板基类成员?,c++,templates,inheritance,c++-faq,C++,Templates,Inheritance,C++ Faq,如果下面的类不是模板,我可以在派生的类中使用x。但是,对于下面的代码,我必须使用this->x。为什么? template <typename T> class base { protected: int x; }; template <typename T> class derived : public base<T> { public: int f() { return this->x; } }; int main() {
派生的类中使用x
。但是,对于下面的代码,我必须使用this->x
。为什么?
template <typename T>
class base {
protected:
int x;
};
template <typename T>
class derived : public base<T> {
public:
int f() { return this->x; }
};
int main() {
derived<int> d;
d.f();
return 0;
}
模板
阶级基础{
受保护的:
int x;
};
模板
派生类:公共基{
公众:
int f(){返回此->x;}
};
int main(){
导出d;
d、 f();
返回0;
}
在继承过程中,x
是隐藏的。您可以通过以下方式取消隐藏:
template <typename T>
class derived : public base<T> {
public:
using base<T>::x; // added "using" statement
int f() { return x; }
};
模板
派生类:公共基{
公众:
using base::x;//添加了“using”语句
int f(){return x;}
};
(原始答案自2011年1月10日起)
我想我已经找到了答案。
答案并不特定于gcc
更新:响应C++11标准:
14.6.2从属名称[临时部门]
[…]
3在类或类模板的定义中,如果基类依赖于
模板参数,在非限定名称期间不检查基类范围
在类模板的定义点查找
或成员,或在类模板或成员的实例化过程中
“因为标准这么说”是否算是一个答案,我不知道。我们现在可以问,为什么标准要求,但正如和其他人所指出的,后一个问题的答案是相当长和有争议的。不幸的是,当涉及到C++标准时,通常几乎不可能给出一个简短而独立的解释,说明为什么标准规定了某些东西;这也适用于后一个问题。简短回答:为了使x
成为一个依赖名称,因此在知道模板参数之前将延迟查找
详细回答:当编译器看到模板时,应该立即执行某些检查,而不查看模板参数。其他的则延迟到参数已知为止。它被称为两阶段编译,MSVC不这样做,但它是标准所要求的,并由其他主要编译器实现。如果愿意,编译器必须在看到模板后立即编译模板(以某种内部解析树表示),并将编译实例化推迟到以后
对模板本身执行的检查,而不是对模板的特定实例化执行的检查,要求编译器能够解析模板中代码的语法
在C++(和C)中,为了解决代码的语法,有时需要知道某个类型是否是一个类型。例如:
#if WANT_POINTER
typedef int A;
#else
int A;
#endif
static const int x = 2;
template <typename T> void foo() { A *x = 0; }
是否为从属名称?对于基类,任何名称都可以出现在基类中。所以我们可以说A是一个依赖名称,并将其视为非类型。这将产生一种不良的效果,即Foo中的每个名称都是依赖的,因此Foo中使用的每个类型(内置类型除外)都必须经过限定。在Foo里面,你必须写:
typename std::string s = "hello, world";
因为std::string
将是一个依赖名称,因此除非另有规定,否则假定为非类型。哎哟
允许首选代码(返回x;
)的第二个问题是,即使在Foo
之前定义了Bar
,并且x
不是该定义中的成员,以后也有人可以为某些类型的Baz
定义Bar
的专门化,这样,Bar
确实有一个数据成员x
,然后实例化Foo
。因此,在该实例化中,模板将返回数据成员,而不是返回全局x
。或者相反,如果Bar
的基本模板定义具有x
,则它们可以在没有它的情况下定义专门化,并且您的模板将查找全局x
以返回Foo
。我认为这被认为和你遇到的问题一样令人惊讶和痛苦,但它只是默默地令人惊讶,而不是抛出一个令人惊讶的错误
为了避免这些问题,该标准实际上规定,除非明确要求,否则不考虑搜索类模板的依赖基类。这使得一切不再依赖,仅仅因为它可以在依赖基中找到。它还具有您所看到的不希望出现的效果—您必须限定基类中的内容,否则就找不到它。使A
独立的常用方法有三种:
使用Bar::A类中的code>现在指的是栏中的某个东西,因此是依赖的
Bar::A*x=0代码>在使用点-同样,A
肯定在栏中。这是一个乘法,因为没有使用typename
,所以可能是一个坏例子,但我们必须等到实例化后才能确定运算符*(Bar::a,x)
是否返回右值。谁知道呢,也许真的
this->A
在使用点-A
是一个成员,因此如果它不在Foo
中,它必须在基类中,标准再次说明这使它具有依赖性
两阶段编译既复杂又困难,并且在代码中引入了一些额外的冗长要求。但与民主一样,它可能是最糟糕的做事方式,与其他方式不同
您可以合理地争辩说,在您的示例中,返回x如果x
是基类中的嵌套类型,则code>没有意义,因此该语言应该(a)说它是依赖名称,并且(2)将其视为非类型,并且您的代码在没有this->
的情况下可以工作。在某种程度上,您是一个问题解决方案的附带损害的受害者,而这个问题不适用于您的情况,但仍然存在一个问题,即您的基类可能会在您下面引入阴影全局的名称,或者没有
typename std::string s = "hello, world";