C++ 的嵌套類模板的特化定義不允許寫在類定義的範圍內

最近在使用在使用模板特化寫一段程序時發現一個奇怪的問題,比如像如下代碼:

  #include <iostream>
using namespace std;
class CMyClass
{
public:
     template <typename T>
     struct test
    
         T i;
     };

     template <>
     struct test<long>
     {
         unsigned long i;
     };
};

int main(void)
{
     CMyClass::test<int> test1;
     CMyClass::test<long> test2;
     CMyClass::test<char> test3;

     cout << "typeid(test1.i) is " << typeid(test1.i).name() << endl;
     cout << "typeid(test2.i) is " << typeid(test2.i).name() << endl;
     cout << "typeid(test3.i) is " << typeid(test3.i).name() << endl;

     return 0;
}

這段代碼在Linux下的GCC 3.4.3下無法編譯通過,編譯時提示錯誤:

xxx.cpp:12: error: invalid explicit specialization before '>' token
xxx.cpp:12: error: explicit specialization in non-namespace scope `class CMyClass'

但在VC6和VC8下都可以編譯通過。

 

後翻閱資料,發現有人提到,C++標準中規定,嵌套類模板在類的定義中不允許被顯示特化聲明,只允許偏特化(“Explicit template specialization is forbidden for nested classes ”,“As partial template specialization is not forbidden ”),比如,這樣就可以:

 

#include <iostream>
using namespace std;

class CMyClass
{
public:
     template <typename T, typename S = void>
     struct test
     {
         T i;
     };
     template <typename S>
     struct test<long, S>
     {
         unsigned long i;
     };
};
int main(void)
{
     CMyClass::test<int> test1;
     CMyClass::test<long> test2;
     CMyClass::test<char> test3;

     cout << "typeid(test1.i) is " << typeid(test1.i).name() << endl;
     cout << "typeid(test2.i) is " << typeid(test2.i).name() << endl;
     cout << "typeid(test3.i) is " << typeid(test3.i).name() << endl;

     return 0;

}

在上面這段代碼使用一個無用的模板參數來實現以偏特代替特化,從而化解了這個問題。至於爲什麼VC下能夠正常編譯,網上的資料說是VC不符合標準(“MSVC is wrong in this case and g++ correct”),不過這點我尚未在C++標準中找到明文依據。

但是這樣一來就有個問題,偏特化在VC6下是用BUG的,無法正常使用,也就是說出來的代碼將無法兼容VC6。對於VC6這樣落伍的編譯器,兼容它是沒有太大的必要,但是回頭想想,難道要在定義嵌套類模板的特化,就不行了麼?必須使用偏特化來代替麼?C++對此是如何規定的呢?翻閱相關資料後,我找到了答案--要把特化的代碼寫在類定義的外面(要寫在namespace下),如第一段代碼應該寫成這樣:

 

#include <iostream>
using namespace std;

class CMyClass
{
public:
     template <typename T>
     struct test
     {
         int i;
     };
};
template <>
struct CMyClass::test<long>
{
     long i;
};

int main(void)
{
     CMyClass::test<int> test1;
     CMyClass::test<long> test2;
     CMyClass::test<char> test3;

     cout << "typeid(test1.i) is " << typeid(test1.i).name() << endl;
     cout << "typeid(test2.i) is " << typeid(test2.i).name() << endl;
     cout << "typeid(test3.i) is " << typeid(test3.i).name() << endl;

     return 0;
}

這樣修改後,就可以在GCC下編譯通過了,同時,VC6,VC8也都能編譯通過!

總結一下吧:
在C++中,如果要對嵌套類模板進行特化,則要麼使用偏特化來替代特化(增加一個無用的模板參數),要麼將特化代碼放在類定義之外。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章