Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么我必须通过this指针访问模板基类成员?_C++_Templates_Inheritance_C++ Faq - Fatal编程技术网

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现在指的是
    栏中的某个东西,因此是依赖的
  • Bar::A*x=0在使用点-同样,
    A
    肯定在
    栏中。这是一个乘法,因为没有使用
    typename
    ,所以可能是一个坏例子,但我们必须等到实例化后才能确定
    运算符*(Bar::a,x)
    是否返回右值。谁知道呢,也许真的
  • this->A
    在使用点-
    A
    是一个成员,因此如果它不在
    Foo
    中,它必须在基类中,标准再次说明这使它具有依赖性
两阶段编译既复杂又困难,并且在代码中引入了一些额外的冗长要求。但与民主一样,它可能是最糟糕的做事方式,与其他方式不同

您可以合理地争辩说,在您的示例中,
返回xx
是基类中的嵌套类型,则code>没有意义,因此该语言应该(a)说它是依赖名称,并且(2)将其视为非类型,并且您的代码在没有
this->
的情况下可以工作。在某种程度上,您是一个问题解决方案的附带损害的受害者,而这个问题不适用于您的情况,但仍然存在一个问题,即您的基类可能会在您下面引入阴影全局的名称,或者没有
typename std::string s = "hello, world";