2010-01-19 79 views
4

我有一些在VC9(Microsoft Visual C++ 2008)中編譯好的模板代碼,但不能在GCC 4.2(在Mac上)編譯。我想知道是否有一些我錯過的語法魔法。調用模板化內部類靜態成員函數的C++語法?

下面我有一個精簡的例子,它演示了我的錯誤。對不起,如果這個例子似乎毫無意義,我儘可能地去除這個錯誤。

特別是我有一個模板類S,它有一個內部類R也是一個模板類。從頂級模板函數foo,我試圖調用[R ::追加其爲R的靜態成員函數:

template< typename C > 
struct S { 
    template< typename T > 
    S<C> & append(const T &) { return *this; } 

    template< int B > 
    struct R { 
     template< typename N > 
     static S<C> & append(S<C> & s, const N) { 
      return s.append(42); 
     } 
    }; 
}; 

template< typename C > 
S<C> & foo(S<C> & s, const int n) { 
    S<C>::R<16>::append(s, n); // error: '::append' has not been declared 
    return s; 
} 

任何人在那裏知道我做錯了嗎?

+0

+1,我想知道這是Visual Studio中的錯誤還是gcc中的錯誤。 – 2010-01-19 07:57:13

+0

顯然這是Visual C++的一個非標準「特性」。 C++規範指出在這種情況下需要消歧。 VC讓你跳過它。我不知道是否有一個編譯器開關讓VC更嚴格地執行規範合規性? – jwfearn 2010-01-21 01:45:22

回答

2

有使用Visual Studio和GCC,這是一個已知的問題:)而且我使用VS2003和gcc 3.4.2,所以一直這樣。

如果我沒有記錯,問題是由於在這些編譯器上解析模板的方式。

GCC的行爲被標準的規定,並執行2個解析:

  • 時遇到的模板,而對類型的任何信息,在這一點上它需要一些typenametemplate魔法來幫助理解第一這是怎麼回事
  • 第二時候,居然用實例從另一方面給定類型

模板,VS只做一名解析,在實例化,因此能充分資源在這裏和那裏添加沒有typenametemplate的符號。

您有方法同樣的事情:

template <class Item> 
struct Test 
{ 
    template <class Predicate> 
    void apply(Predicate pred); 

    void doSomething { this->apply(MyPredicate()); }   // Visual Studio 
    void doSomething { this->template apply(MyPredicate()); } // gcc 
}; // struct Test 

關於同一主題,如果你這樣做:

template <class Item> 
struct Test { static const std::string Name; }; 

實際上你需要定義此static屬性對的每個實例模板,否則你將會有一個未定義的符號。

VS接受此語法:

const std::string Test<MyType>::Name = "MyType"; 

但GCC詢問了一點關鍵字:

template <> const std::string Test<MyType>::Name = "MyType"; 

,因爲它要求較少的你,但在另一方面,你可能會覺得VS的更好一旦第一次解析模板方法/類時,gcc可能會警告你模板方法/類中的錯誤(即沒有任何實例化),並且個人越早越好。

顯然,好消息是,如果編譯gcc(對於這些問題),它也可以在Visual Studio上很好地編譯。

雖然我不是標準主義者,但我不確定標準究竟是要求還是建議2分析方案。

+0

該標準確實要求對非依賴名稱和從屬名稱進行兩階段查找。 – ephemient 2010-01-19 19:18:14

+0

有人有一個膠水,MSVC版本降低了這個默默無聞,並實現標準?很明顯,代碼可能需要不穩定的宏來保持不僅跨平臺,而且跨越MSVC版本。 – lef 2014-11-24 23:40:19

0

嘗試在struct R :: append中寫入「const int N」,然後使用N(而不是42?)。

4

我得到它的編譯:

template< typename C > 
S<C> & foo(S<C> & s, const int n) { 
    typedef typename S<C>::template R<16> SR; 
    SR::append(s, n); 
    return s; 
} 
+0

typedef不是必需的,但我也喜歡將複雜類型別名,即使我只使用它們一次,只是爲了清楚起見。 – 2010-01-19 10:00:37

4

你必須告訴編譯器將從屬名稱R是一個模板:

template< typename C > 
S<C> & foo(S<C> & s, const int n) { 
    S<C>::template R<16>::append(s, n); 
    return s; 
} 
+0

所以你認爲VC通過接受原始代碼是不符合的? – 2010-01-19 08:02:36

+0

感謝您的幫助。這確實修復了我在Mac上的錯誤。我修復了我的實際項目,並且現在可以在Mac和PC上構建。我不確定哪個編譯器更符合要求。 – jwfearn 2010-01-19 08:18:59

+0

@Terry:我發佈了一個實際解釋事情的答案。我認爲gcc實際上更符合這一點,但我可能是錯的。 – 2010-01-19 10:16:37