2012-05-15 2 views
6

Этой проблема немного трудно объяснить, так что я начну с примером:шаблон специализации для подклассов шаблона базового класса

У меня есть шаблон класса, который принимает тип и целочисленную константу в качестве параметров шаблона, и у меня есть ряд дочерних классов, производные от конкретизации этого шаблона:

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

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

Я хочу использовать эти классы с каким-либо другим шаблоном (назовем его Test), который имеет специализации для различных типов. Поскольку поведение должно быть точно таким же для всех классов, которые получены из любого экземпляра базы, я хочу определить только одну специализацию Test, которая обрабатывает все классы, полученные из Base.

Я знаю, что не могу непосредственно специализироваться на Base < V, i >, потому что это не будет определять дочерние классы. Вместо этого, мой первый подход с использованием enable_if и типа черты BOOST в:

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

А потом он компилируется.

Теперь, мой вопрос: Как это сделать без изменения базового класса? В этом случае это было возможно, потому что я написал его сам, но что я могу сделать, если мне придется обрабатывать код библиотеки сторонних разработчиков в моем тестовом шаблоне? Есть ли более элегантное решение?

Редактировать: Кроме того, может кто-нибудь дать мне подробное объяснение, почему именно первый подход не работает? У меня есть общая идея, но я предпочел бы иметь правильное понимание. :-)

ответ

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

[Edited] в коде 3 партии, вы можете использовать трюк, как:

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); } 
} 
Смежные вопросы