簡體   English   中英

成員函數聲明簽名中的類成員可見性

[英]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))
    {
    }
};

但這不是( af交換位置):

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被之前聲明fa被發現指一類構件。
  • 通過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; 然后兩個聲明, xy將使用依賴類型,但都不需要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一個prvalue X ”可選的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM