[英]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.