Специализация шаблона для подклассов базового класса шаблона

Эту проблему немного сложно объяснить, поэтому я начну с примера:

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

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, потому что это не обнаружит дочерние классы. Вместо этого мой первый подход заключался в использовании свойств enable_if и type в 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;
}

Идея заключалась в том, что специализация должна обрабатывать все классы, производные от 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();
    }
};

И затем она компилируется.

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

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

6
задан Benjamin Schug 15 May 2012 в 13:36
поделиться