Эту проблему немного сложно объяснить, поэтому я начну с примера:
У меня есть шаблон класса, который принимает тип и целочисленную константу в качестве параметров шаблона, и я иметь несколько дочерних классов, производных от экземпляров этого шаблона:
template <class V, int i>
struct Base
{
static void doSomething() { cout << "something " << i << endl; };
};
struct Child : public Base<int,12>
{
};
Я хочу использовать эти классы с каким-то другим шаблоном (назовем его Test), который имеет специализации для разных типов. Поскольку поведение должно быть одинаковым для всех классов, производных от любого экземпляра Base, я хочу определить только одну специализацию Test, которая обрабатывает все классы, производные от Base.
Я знаю, что не могу напрямую специализироваться на Base
// 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;
}
Идея заключалась в том, что специализация должна обрабатывать все классы, производные от Base, с любыми произвольными значениями V и i. Это не работает, gcc жалуется:
error: template parameters not used in partial specialization: error: ‘V’ error: ‘i’
Думаю, проблема в том, что этот подход потребует от компилятора перепробовать все возможные комбинации V и i, чтобы проверить, совпадают ли какие-либо из них. На данный момент я решил проблему, добавив кое-что в базовый класс:
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();
}
};
И затем она компилируется.
Теперь мой вопрос: Как я могу сделать это, не изменяя базовый класс?В этом случае это было возможно, потому что я написал это сам, но что я могу сделать, если мне приходится обрабатывать сторонний класс? код библиотеки в моем тестовом шаблоне такой? Есть ли более элегантное решение?
Редактировать: Кроме того, может ли кто-нибудь дать мне подробное объяснение, почему именно первый подход не работает? У меня есть приблизительное представление, но я бы предпочел правильное понимание. :-)