2 回答
TA贡献1840条经验 获得超5个赞
C ++答案(一般答案)
考虑一个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; }};
Qanswer(具体答案)
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
在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>
作为一个已知的完整类的处理有时和作为一个未知的类在其他时候不一致,那么,你是对的。
TA贡献1744条经验 获得超4个赞
我猜它应该与Cleanup()例程的重载使用有关。传递的类型由模板类型T显式控制,而模板类型T又可以控制调用哪个重载版本的Cleanup()。
- 2 回答
- 0 关注
- 604 浏览
添加回答
举报