2012-05-15 25 views
6

Bu sorun açıklamak biraz zor, bu yüzden bir örnekle başlayacak: Bir tür ve şablon parametreleri olarak sabit bir tamsayı alan bir sınıf şablonu varŞablon uzmanlık

ve o şablonun örneklemesi kaynaklanıyor çocuk sınıfları bir dizi var:

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

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

bazı diğer şablonu ile bu sınıfları kullanmak istediğiniz farklı türleri için uzmanlık sahip olan (en Testi diyelim). Davranış, Base'in herhangi bir örneğinden türetilen tüm sınıflar için tam olarak aynı olması gerektiğinden, yalnızca Temel'den türetilen tüm sınıfları işleyen tek bir Uzmanlık özelleştirme tanımlamak istiyorum.

Temel olarak, bu alt sınıfları algılayamayacağından, < V, i > numaralı Temel için uzmanlaşamayacağımı biliyorum. Bunun yerine, benim ilk yaklaşım Boost enable_if ve tip özellikleri kullanıyordu:

// 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; 
} 

fikri uzmanlık V ve i herhangi keyfi değerlerle Base türetilen tüm sınıfları işlemek gerektiği şeklindeydi. Bu işe yaramazsa, gcc yakınır:

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

Sorun bu yaklaşım V tüm olası kombinasyonları denemek derleyici gerektirecektir ve bunlardan herhangi eşleşirse ı kontrol etmek olduğunu tahmin ediyorum.

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

Bu şekilde, uzmanlaşma artık V olması gerekir ve ben serbest şablon parametreleri:

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(); 
    } 
}; 

Sonra Şimdilik, ben temel sınıf için bir şeyler ekleyerek bu sorunu çalıştı derler.

Şimdi, sorum şu: Temel sınıfı değiştirmeden bunu nasıl yapabilirim? Bu durumda, kendim yazdığım için mümkün oldu, ancak Test şablonumda üçüncü taraf kitaplık kodunu işlemek zorunda kalırsam ne yapabilirim? Daha şık bir çözüm var mı?

Düzenleme: Birisi bana tam olarak ilk yaklaşımın neden çalışmadığını ayrıntılı bir açıklama verebilir mi? Zor bir fikrim var, ama doğru bir anlayışa sahip olmayı tercih ederim. :-)

basit bir çözüm Base izin vermektir

cevap

3

başka Base_base devralır:

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 ipucu için teşekkürler, bu benim mevcut kodumu biraz daha okunabilir kılıyor. Ancak, üçüncü parti kütüphaneler için, bu çalışma, en azından çocuk sınıfları da kütüphaneye ait değilse işe yaramaz. –

+1

@BenjaminSchug: belki de [this] (http://stackoverflow.com/a/6398983/1324131) yeni sorunuzu düzenlenmiş soruda cevaplıyor. – user2k5

+0

Teşekkürler, bu, ilk yaklaşımın neden çalışmadığını açıklıyor.Benim bağırsak hislerimin doğru olması gibi görünüyor. –

0

:

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(); 
    } 
}; 

[ Düzenlendi] 3. taraf kodunda, aşağıdaki gibi bir hile kullanabilirsiniz İşlev aşırı yüklenmesini ve 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); } 
} 
İlgili konular