2010-10-24 116 views
29

爲什麼不能將C++編譯器識別g()b被繼承的Superclass成員在此代碼所示:訪問超類的保護成員在C++中使用模板

template<typename T> struct Superclass { 
protected: 
    int b; 
    void g() {} 
}; 

template<typename T> struct Subclass : public Superclass<T> { 
    void f() { 
    g(); // compiler error: uncategorized 
    b = 3; // compiler error: unrecognized 
    } 
}; 

如果我簡化Subclass和剛剛繼承然後它編譯。當完全符合g()時,它也編譯爲Superclass<T>::g()Superclass<T>::b。我正在使用LLVM GCC 4.2。

注意:如果我在超類中公開g()b,它仍會失敗並出現相同的錯誤。或者

template<typename T> struct Subclass : public Superclass<T> { 
    void f() { 
    this->g(); 
    this->b = 3; 
    } 
}; 

,正如你:

回答

39

這可以通過使用using拉的名字到當前範圍修改爲:

template<typename T> struct Subclass : public Superclass<T> { 
    using Superclass<T>::b; 
    using Superclass<T>::g; 

    void f() { 
    g(); 
    b = 3; 
    } 
}; 

或通過該0​​指針訪問限定名稱已經注意到,通過限定全名。

這是必要的原因是,C++不考慮名稱解析的超類模板(因爲它們是從屬名稱和從屬名稱不予考慮)。它在您使用Superclass<int>時起作用,因爲它不是模板(它是實例化模板),因此它的嵌套名稱不是,而是依賴於的名稱。

+6

微軟的編譯器不服從這條規則。我很生氣 – 2010-10-24 21:06:24

+0

謝謝,那有效。 – andrewz 2010-10-25 00:44:11

+8

@阿門:只有這一個規則?哇,它真的*變得更好了。 :-D – 2010-10-25 06:04:18

14

Konrad的回答並沒有要求或回答最終的「爲什麼」。這不僅僅是C++委員會任意說「嘿,放棄依賴名字,反正沒有人喜歡它們」。相反,編譯器甚至在實例化之前對模板進行了一些檢查,並且直到它知道T之前,它才具有g()或b的意義,因爲它通常不能 - 通常 - 在可能的特化之間進行選擇基類(例如SuperClass<X>可能有int b,而SuperClass<Y>void b()SuperClass<Z>根本沒有b)。更明確的形式只是說「相信我 - 在實例化時它必須來自基類」(否則會出現編譯器錯誤)「。

+0

有點有道理,但它絕對是模板的一個比較模糊的「特徵」。 – ChrisWue 2014-01-08 22:06:22

+0

@ChrisWue:這場比賽有很多比賽! ;-) – 2014-01-09 02:47:23

+0

GCC能夠在過去的20世紀90年代處理這個問題,但隨着C++的發展,它發生了變化。我認爲在某些情況下處理它的方式會造成問題,這就是爲什麼它發生了變化。 – PolarBear2015 2017-02-06 14:32:28