2011-10-18 102 views
3

爲什麼有些編譯器會堅持模板基類的合格成員公共成員,而對非模板類不要求相同呢?請看下面代碼:模板類vs私有繼承

模板類:

#include <iostream> 

using namespace std; 

template <class T> 
class TestImpl { 
public: // It wont make a difference even if we use a protected access specifier here 
    size_t vval_; 
    TestImpl(size_t val = 0) : vval_(val) { } 
}; 

template <class T> 
class Test : public TestImpl<T> { 
public: 
    Test(size_t val) : TestImpl<T>(val) { 
     cout << "vval_ : " << vval_ << endl; // Error: vval_ was not declared in this scope 
     //! cout << "vval_ : " << TestImpl<T>::vval_ << endl; // this works, obviously 
    } 
}; 

int main() { 
    Test<int> test1(7); 

    return 0; 
} 

非模板類:

#include <iostream> 

using namespace std; 

class TestImpl { 
public: // It wont make a difference even if we use a protected access specifier here 
    TestImpl(size_t val = 0) : vval_(val) {} 
    size_t vval_; 
}; 

class Test : public TestImpl { 
public: 
    Test(size_t val) : TestImpl(val) { 
     cout << "vval_ : " << vval_ << endl; 
    } 
}; 

int main() { 
    Test test1(7); 

    return 0; 
} 

在上面的代碼之間的顯著差別是,雖然第一個列表使用模板類,第二個列表不。

現在,這兩個列表將編譯罰款與微軟的Visual Studio編譯器(CL),但首批上市WONT同時與數字火星編譯(DMC)和編譯簡約GNU爲Windows(MinGW的 - g ++)編譯器。我會得到一個錯誤,如「vval_未在範圍中聲明」 - 一個錯誤,我很明白它的含義。

如果我有資格獲得使用TestImpl <牛逼> :: vval_代碼工作的TestImpl的公共變量vval_。在第二個列表中,當派生類訪問基類'vval_變量而不限定它時,編譯器不會抱怨。

對於兩種編譯器可能還有其他人,我的問題是,爲什麼我應該能夠從非模板類與非模板類繼承直接訪問(不符合條件)vval_變量直接,而我不能從做同樣的模板類繼承自模板類

+2

您也可以限定'vval_':'this-> vval_'。 –

+2

http://www.parashift.com/c++-faq-lite/templates.html#faq-35.19 – visitor

+0

@Rob:this-> vval_適用於派生類。當使用「以某種類型的繼承來實現」時,如果您的實現的重要部分位於基礎claas中,那麼只需要執行此操作 - 每個地方都會感到不便。謝謝 –

回答

2

的問題,你所面臨的是編譯器vval_不是一個從屬名稱,所以它會嘗試在模板實際實例化之前查找它。此時,編譯器[*]還不知道基本類型,因此它不考慮模板化基礎。 Visual Studio不執行兩階段查找,因此這不是必需的。

解決方案正在將標識符轉換爲依賴標識符,可以通過多種方式之一完成。最簡單的建議將使用this(如this->vval_)。通過添加明確的this,編譯器知道vval_可以根據模板參數而有所不同,現在它是一個從屬名稱,並且它將查找推遲到第二階段(在參數替換之後)。

或者,您可以使用TestImpl<T>::vval_來限定標識符所屬的類型,如@mrozenau所示。再次,這使得標識符依賴於模板參數T並且查找被推遲。雖然這兩種方法都有助於延遲查找的最終目的,但第二種方法會帶來額外的副作用,即動態分派將被禁用。在這種情況下,這並不重要,但如果vval_實際上是虛擬功能,那麼this->f()將調用最終的覆蓋,而TestImpl<T>::f()將執行TestImpl<T>中的覆蓋。

[*]在模板的第一階段驗證過程中,在將參數替換爲模板之前,基本類型尚未知。原因是不同的參數集可能會觸發選擇基礎模板的不同特化。

6

必須限定vval_TestImpl<T>告訴它Test<T>取決於實際類型的T編譯器(可能存在的Test<T>定義之前宣佈TestImpl<T>一些局部/明確的專業化和它的實例,將改變的vval_在這方面的意思。爲了讓編譯器知道的是,你必須告訴vval_是(模板參數)相關。

參見http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html

3

MSVC(微軟...)從來就不是符合標準,當涉及到的模板代碼,所以他是鶴立雞羣:)

的問題是,模板在兩個階段解析:

  • 當模板被解析時,執行第一階段,並且當所述模板被實例化
不依賴(明確地)上模板參數的任何標識符,應解決
  • 執行第二階段

    在你的情況下,第一階段失敗,因爲vval_沒有明確依賴模板參數(不是依賴名稱),因此應該可用。

    一個簡單的補救措施是限定vval_,通常與this->,將其標記爲從屬。

  • +0

    實際上這並不是那麼糟糕,唯一缺少的東西(標準要求)是兩階段名稱查找。並不要求是兩階段解析,例如,人們可以想象編譯器的實現,它只會記住模板定義處的上下文,並將其用於實例化時的專有名稱查找。 –

    +0

    @KonstantinOznobihin:我不同意*不那麼差*。名稱查找類似於重載分辨率:對於總是選擇最後可能的重載的編譯器會怎麼說?我明白他們可能不希望用戶使用依賴名稱問題來進行可用性,但是您可以在不中斷查找的情況下執行此操作:x –

    +0

    @KonstantinOznobihin:嗯...標準確定模板上下文中的依賴類型必須前面加上'typename'關鍵字(或者帶有'template'關鍵字的依賴模板),並且VS忽略該要求,這使得編寫非可移植代碼變得非常容易...這一切都取決於你如何看待它 –