Этой проблема немного трудно объяснить, так что я начну с примером:шаблон специализации для подклассов шаблона базового класса
У меня есть шаблон класса, который принимает тип и целочисленную константу в качестве параметров шаблона, и у меня есть ряд дочерних классов, производные от конкретизации этого шаблона:
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();
}
};
А потом он компилируется.
Теперь, мой вопрос: Как это сделать без изменения базового класса? В этом случае это было возможно, потому что я написал его сам, но что я могу сделать, если мне придется обрабатывать код библиотеки сторонних разработчиков в моем тестовом шаблоне? Есть ли более элегантное решение?
Редактировать: Кроме того, может кто-нибудь дать мне подробное объяснение, почему именно первый подход не работает? У меня есть общая идея, но я предпочел бы иметь правильное понимание. :-)
Спасибо за подсказку Base_Base, что делает мой текущий код немного более удобочитаемым. Однако для сторонних библиотек это не сработает, по крайней мере, если дочерние классы также принадлежат библиотеке. –
@BenjaminSchug: возможно [это] (http://stackoverflow.com/a/6398983/1324131) отвечает на ваш новый вопрос в редактируемом вопросе. – user2k5
Спасибо, это объясняет, почему первый подход не сработал.Похоже, мое чувство кишки было правильным. –