[英]Class member visibility in member function declaration signature
為什么這樣做:
template <typename A>
struct S {
A a;
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
};
但這不是( a
和f
交換位置):
template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
A a;
};
說a
未在該范圍內聲明(在decltype內),但添加顯式this->
使其工作。
template <typename A>
struct S {
A a;
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
};
這是有效的,因為在尾隨返回類型中,周圍類的成員是可見的。 不是所有成員,而只是在它之前聲明的成員(在尾隨返回類型中,該類不被認為是完整的,而不是函數體)。 那么這里做了什么:
a
是否依賴。 由於a
被之前聲明f
, a
被發現指一類構件。 通過C ++中的模板規則,發現a
引用當前實例化的成員,因為它是周圍模板的實例化的成員。 在C ++中,這個概念主要用於決定名稱是否依賴:如果已知名稱引用周圍模板的成員,則在實例化時不一定需要查找,因為編譯器已經知道模板的代碼(用作從它實例化的類類型的基礎!)。 考慮:
template<typename T> struct A { typedef int type; void f() { type x; A<T>::type y; } };
在C ++ 03中,聲明y
的第二行將是一個錯誤,因為A<T>::type
是一個依賴名稱,並且在它前面需要一個typename
。 只有第一行被接受了。 在C ++ 11,這種不一致性是固定的,這兩個類型的名稱都是非相關的,並且不會需要一個typename
。 如果將typedef更改為typedef T type;
然后兩個聲明, x
和y
將使用依賴類型,但都不需要typename
,因為你仍然命名當前實例化的成員,並且編譯器知道你命名一個類型。
a
是當前實例化的成員。 但它是依賴的,因為用於聲明它的類型( A
)是依賴的。 但是,這在您的代碼中無關緊要。 無論相關與否, a
是發現和代碼有效。 template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
A a;
};
在此代碼中,再次查找a
以查看它是否依賴和/或它是否是當前實例化的成員。 但由於我們上面了解到成員尾隨返回類型后聲明是不可見的,我們無法找到一個聲明a
。 在C ++中,除了“當前實例化的成員”之外,還有另一個概念:
未知專業的成員 。 此概念用於指代名稱可能改為引用依賴於模板參數的類成員的情況。 如果我們訪問過B::a
,那么a
將是未知專門化的成員,因為在實例化時替換B
時, 未知什么聲明是可見的。
既不是當前的成員,也不是未知專業的成員。 所有其他名稱都是如此。 你的情況適合在這里 ,因為眾所周知, a
永遠不能實例化時發生的任何實例化的成員(記住這個名字查找無法找到a
,因為它是后宣布f
)。
由於a
不依賴於任何規則,因此未找到任何聲明的查找是綁定的 ,這意味着在實例化中沒有可以找到聲明的其他查找。 在模板定義時查找非依賴名稱。 現在GCC正確地給你一個錯誤(但請注意,一如既往,不需要立即診斷出錯誤的模板)。
template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(this->a.f(b))
{
}
A a;
};
在這種情況下,您添加了this
並接受了GCC。 這個名字a
遵循this->
再次查找在看它是否可能是當前實例中的一員。 但是,由於尾隨返回類型中的成員可見性,因此未找到任何聲明。 因此,該名稱被視為不是當前實例化的成員。 由於在實例化時沒有辦法, S
可以擁有a
可以匹配的附加成員(沒有依賴於模板參數的S
基類),該名稱也不是未知專業化的成員。
同樣,C ++沒有規則可以使this->a
依賴。 但是它使用this->
,因此名稱必須在實例化時引用S
某個成員! 所以C ++標准說
類似地,如果對象表達式的類型是當前實例化的類成員訪問表達式中的id-expression不引用當前實例化的成員或未知專業化的成員,則該程序甚至是不正確的如果未實例化包含成員訪問表達式的模板; 無需診斷。
同樣,此代碼不需要診斷(GCC實際上不會給出它)。 成員訪問表達式this->a
中的id-expression a
依賴於C ++ 03,因為該標准中的規則沒有像C ++ 11那樣詳細和精細。 讓我們想象一下C ++ 03有decltype
和尾隨返回類型。 這意味着什么?
this->a
將是依賴的 S<SomeClass>
實例化查找將失敗,因為在實例化時不會找到 - this->a
(正如我們所說,尾隨返回類型不會看到稍后聲明的成員)。 因此,C ++ 11對該代碼的早期拒絕是好的和有用的。
編譯成員函數的主體,就好像它是在類之后定義的一樣。 因此,在該類中聲明的所有內容都在此范圍內。
但是,函數的聲明仍然在類聲明中,並且只能看到它之前的名稱。
template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(a.f(b)); // error - a is not visible here
A a;
};
template <typename A>
template <typename B>
auto S<A>::f(B b) ->
decltype(a.f(b))
{
return a.f(b); // a is visible here
}
標准說(第14.6.2.1節):
如果對於給定的模板參數集,實例化了模板的特化,該模板的特殊化引用了具有qualified-id或類成員訪問表達式的當前實例化的成員,則qualified-id或類成員訪問表達式中的名稱為在模板實例化上下文中查找。
this->a
是類成員訪問表達式,因此應用此規則並在實例化時進行查找,其中
。 S<A>
完成
最后,這根本不能解決您的問題,因為第5.1.1節說:
如果聲明聲明一個類X的成員函數或成員函數模板, 表達
this
是類型“指向cv限定符SEQ一個prvalueX
”可選的CV-限定符-SEQ和函數定義的端部之間, 成員聲明者或聲明者 。 它不應出現在可選的cv-qualifier-seq之前 ,它不應出現在靜態成員函數的聲明中(盡管它的類型和值類別是在靜態成員函數中定義的,因為它們在非靜態成員函數中) 。
所以你不能在這里使用this->
,因為它是在函數聲明的cv-qualifier-seq部分之前。
等等,不,不是! 第8.4.1節說
函數定義中的聲明符應具有該形式
D1 (
parameter-declaration-clause
)
cv-qualifier-seq opt ref-qualifier opt exception-specification opt attribute-specifier-seq opt trailing-return-type opt
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.