1. 程式人生 > >C++ 的巢狀類模板的特化定義不允許寫在類定義的範圍內

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++中,如果要對巢狀類模板進行特化,則要麼使用偏特化來替代特化(增加一個無用的模板引數),要麼將特化程式碼放在類定義之外。