[英]In a templated derived class, why do I need to qualify base class member names with "this->" inside a member function?
当我调查 Qt 的源代码时,我看到 trolltech 人员明确使用this
关键字来访问析构函数上的字段。
inline ~QScopedPointer()
{
T *oldD = this->d;
Cleanup::cleanup(oldD);
this->d = 0;
}
那么,这种用法有什么意义呢? 有什么好处吗?
编辑:对于那些投票结束这个问题的人,我怀疑这种用法是用于某些类继承的情况
QScopedPointer 类定义的一部分:
template <typename T, typename Cleanup = QScopedPointerDeleter<T> >
class QScopedPointer
考虑使用模板基类Derived
的模板类:
template <typename T>
class Base {
public:
int d;
};
template <typename T>
class Derived : public Base<T> {
void f () {
this->d = 0;
}
};
this
具有类型Derived<T>
,一种依赖于T
的类型。 所以this
有一个依赖类型。 所以this->d
使d
成为依赖名称。 从属名称在模板定义的上下文中作为非从属名称和在实例化的上下文中进行查找。
如果没有this->
,名称d
只会作为非依赖名称被查找,而不会被找到。
另一种解决方案是在模板定义本身中声明d
:
template <typename T>
class Derived : public Base<T> {
using Base::d;
void f () {
d = 0;
}
};
d
是QScopedPointer
的成员。 它不是继承的成员。 this->
在这里不是必需的。
OTOH, QScopedArrayPointer
是模板类, d
是模板基类的继承成员:
template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
class QScopedArrayPointer : public QScopedPointer<T, Cleanup>
所以这里- this->
是必要的:
inline T &operator[](int i)
{
return this->d[i];
}
很容易看出,将this->
放在任何地方更容易。
我想所有 C++ 用户都不清楚为什么在非依赖基类中查找名称而不是在依赖基类中查找名称:
class Base0 {
public:
int nd;
};
template <typename T>
class Derived2 :
public Base0, // non-dependent base
public Base<T> { // dependent base
void f () {
nd; // Base0::b
d; // lookup of "d" finds nothing
f (this); // lookup of "f" finds nothing
// will find "f" later
}
};
除了“标准这么说”之外还有一个原因:模板中名称绑定的工作方式的原因。
当模板被实例化时,模板可以具有后期绑定的名称:例如f
in f (this)
。 在Derived2::f()
定义点,编译器不知道变量、函数或类型名称f
。 此时f
可以引用的已知实体集是空的。 这不是问题,因为编译器知道它稍后会查找f
作为函数名或模板函数名。
OTOH,编译器不知道如何处理d
; 它不是(被调用的)函数名。 无法对非(被调用)函数名称进行后期绑定。
现在,所有这些看起来像是编译时模板多态性的基本知识。 真正的问题似乎是:为什么不是d
势必Base<T>::d
在模板定义的时间?
真正的问题是在模板定义时没有Base<T>::d
,因为当时没有完整的类型Base<T>
: Base<T>
已声明,但未定义! 你可能会问:这个怎么办:
template <typename T>
class Base {
public:
int d;
};
它看起来像一个完整类型的定义!
实际上,在实例化之前,它看起来更像是:
template <typename T>
class Base;
到编译器。 不能在类模板中查找名称! 但仅限于模板特化(实例化)。 模板是使模板特化的工厂,模板不是模板特化的集合。 编译器可以查找d
在Base<T>
对于任何特定的类型T
,但它不能查找d
在类模板Base
。 在确定类型T
之前, Base<T>::d
仍然是抽象Base<T>::d
; 仅当类型T
已知时, Base<T>::d
开始引用int
类型的变量。
这样做的结果是类模板Derived2
有一个完整的基类Base0
但是一个不完整的(前向声明的)基类Base
。 仅对于已知类型T
,“模板类”(类模板的特化) Derived2<T>
具有完整的基类,就像任何普通类一样。
你现在看到:
template <typename T>
class Derived : public Base<T>
实际上是一个基类规范模板(一个制作基类规范的工厂),它遵循与模板内的基类规范不同的规则。
备注:读者可能已经注意到,我在解释的末尾编造了一些短语。
这是非常不同的:这里d
是Derived<T>
的限定名称,而Derived<T>
是依赖的,因为T
是模板参数。 限定名称可以是后期绑定的,即使它不是(被调用的)函数名称。
另一个解决方案是:
template <typename T>
class Derived : public Base<T> {
void f () {
Derived::d = 0; // qualified name
}
};
这是等价的。
如果您认为在Derived<T>
的定义中,有时将Derived<T>
视为已知的完整类,而有时将其视为未知的类,则不一致,那么,您是对的。
我猜这与 Cleanup() 例程的过载使用有关。 传递的类型由模板类型 T 显式控制,而模板类型 T 又可以控制调用 Cleanup() 的哪个重载版本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.