2012-05-15 69 views
6

此問題是有點難以解釋,所以我將用一個例子開始:爲模板基類的子類模板專業化

我有一個類模板,需要一個類型和一個整數常數作爲模板的參數,和我有很多的子類,從該模板的實例得出:

template <class V, int i> 
struct Base 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

struct Child : public Base<int,12> 
{ 
}; 

我想使用這些類與其他一些模板(姑且稱之爲測試),這對於不同類型的專業。因爲對於從Base的任何實例派生的所有類,行爲應該完全相同,所以我只想定義一個Test的專門化特性來處理從Base派生的所有類。

我知道我不能直接專注於基地< V,我>因爲這不會檢測子類。相反,我的第一種方法是使用Boost的enable_if和類型特點:

// empty body to trigger compiler error for unsupported types 
template <class T, class Enabled = void> 
struct Test { }; 

// specialization for ints, 
// in my actual code, I have many more specializations here 
template <class Enabled> 
struct Test <int, Enabled> 
{ 
    static void test (int dst) 
    { 
     cout << "Test<int>::test(" << dst << ")" << endl; 
    } 
}; 

// this should handle all subclasses of Base, 
// but it doesn't compile 
template <class T, class V, int i> 
struct Test <T, typename enable_if <is_base_and_derived <Base <V,i>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

int main (int argc, char **argv) 
{ 
    Test <int>::test (23); 
    Test <Child>::test (Child()); 
    return 0; 
} 

的想法是專業化應該處理這些從基地派生與V和我的任意值的所有課程。這不起作用,GCC抱怨:

 
error: template parameters not used in partial specialization: 
error:   ‘V’ 
error:   ‘i’ 

我想這個問題是,這種方法需要編譯器嘗試v的所有可能的組合,我來檢查其中是否匹配。現在,我工作圍繞這一問題通過添加一些基類:

template <class V, int i> 
struct Base 
{ 
    typedef V VV; 
    static constexpr int ii = i; 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

這樣,專業化不再需要擁有V和我的免費模板參數:

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

然後它編譯。

現在,我的問題是:如何在不修改基類的情況下做到這一點?在這種情況下,這是可能的,因爲我自己寫了它,但是如果必須在我的Test模板中處理第三方庫代碼,我該怎麼辦?有沒有更優雅的解決方案?

編輯另外,有人可以給我一個詳細的解釋,爲什麼第一種方法不工作?我有一個粗略的想法,但我寧願有一個正確的理解。 :-)

回答

3

一個簡單的解決方法是讓Base繼承另一個Base_base

struct Base_base 
{}; 

template <class V, int i> 
struct Base 
: public Base_base 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base_base, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

[編輯在第三方代碼,你可以像使用一招:

template <class V, int i> 
struct Base3rdparty 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

template <class V, int i> 
struct Base 
: public Base3rdparty<V, i> 
{ 
    typedef V VV; 
    static constexpr int ii = i; 
}; 

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 
+0

感謝Base_Base提示,它使我當前的代碼更具可讀性。但是,對於第三方庫,這不起作用,至少在子類同樣屬於庫時不起作用。 –

+1

@BenjaminSchug:也許[這](http://stackoverflow.com/a/6398983/1324131)在編輯的問題中回答你的新問題。 – user2k5

+0

謝謝,這解釋了爲什麼第一種方法不起作用。似乎我的直覺是正確的。 –

0

使用功能過載和decltype

// Never defined: 
template<typename T> std::false_type is_Base(T&); 
template<class V, int I> std::true_type is_Base(Base<V,I>&); 

template<typename IsBase, typename T> struct TestHelper; 
template<typename T> struct TestHelper<std::true_type, T> 
{ 
    static void test(const T& dst) { dst.doSomething(); } 
}; 
template<> struct TestHelper<std::false_type, int> 
{ 
    static void test(int dst) 
    { std::cout << "Test<int>::test(" << dst << ")" << std::endl; } 
}; 
// ... 

template<typename T> struct Test 
{ 
    static void test(const T& dst) 
    { TestHelper<decltype(is_Base(std::declval<T&>())), T>::test(dst); } 
}