Частичная специализация шаблоном класса C++

Появилась достаточно неоднозначная задача. Есть абстрактный модуль "Фильтр":

template <typename T, typename DelayLineType>
class FilterAbstract
{
public:
    explicit FilterAbstract(uint32_t length);
    explicit FilterAbstract(const FilterAbstract &object);
    FilterAbstract &operator=(const FilterAbstract &object);
    virtual ~FilterAbstract() = default;

    virtual T Step(const T &value) = 0;

    DelayLineType DelayLine;
    Vector<T> Coefficients;
};

Далее я наследую абстрактный класс с частичной специализацией:

template <typename T>
class FilterOnCircularVectors : public FilterAbstract<T, CircularVector<T>>
{
public:
    explicit FilterOnCircularVectors(uint32_t length);
    explicit FilterOnCircularVectors(const FilterAbstract<T, CircularVector<T>> &object);

    T Step(const T &value) override;

};

Теперь я помимо такого наследования хочу обеспечить частичную специализацию путем использования класса Complex<T> и тут появляется недопонимание как это реализовать правильно с одним и тем же именем в том же файле:

template <template T>
class FilterOnCircularVectors : public FilterAbstract<T, CircularVector<Complex<T>>>
{
public:
    explicit FilterOnCircularVectors(uint32_t length);
    explicit FilterOnCircularVectors(const FilterAbstract<T, CircularVector<Complex<T>>> &object);

    Complex<T> Step(const Complex<T> &value) override;

};

Есть какие-то мысли на этот счет? Плодить классы с пометкой Complex не очень хочется.

UPD: В VS2019 если создать два класса с такими именами и таким наследованием, то возникает ошибка из-за множественного определения. Дело в том, что разное поведение должно быть у функции Step при CircularVector<T> и CircularVector<Complex<T>>


Ответы (3 шт):

Автор решения: Кирилл Чуркин

В последнем примере вы изменяете возвращаемый и принимаемый тип. В базовом классе у вас инстанцируется

virtual T Step(const T &value) = 0;

А вы хотите

virtual Complex<T> Step(const Complex<T> &value) = 0;

Для того, чтобы было последнее вы должны подавать первым аргументом в шаблон не T, а Complex<T>, тогда всё должно работать

template <typename T>
class FilterOnCircularVectors : public FilterAbstract<Complex<T>, CircularVector<Complex<T>>>
{
public:
    explicit FilterOnCircularVectors(uint32_t length);
    explicit FilterOnCircularVectors(const FilterAbstract<Complex<T>, CircularVector<Complex<T>>> &object);

    Complex<T> Step(const Complex<T> &value) override;

};

Заранее предупреждаю, что в данном случае вы не сможете воспользоваться полиморфизмом при подаче разных типов.

Если же вы хотите именно частичную специализацию, то нужно сделать так

template <typename T, typename DelayLineType>
class FilterAbstract<Complex<T>, DelayLineType>
{
public:
    explicit FilterAbstract(uint32_t length);
    explicit FilterAbstract(const FilterAbstract &object);
    FilterAbstract &operator=(const FilterAbstract &object);
    virtual ~FilterAbstract() = default;

    virtual Complex<T> Step(const Complex<T> &value) = 0;

    DelayLineType DelayLine;
    Vector<T> Coefficients;
};
→ Ссылка
Автор решения: AR Hovsepyan

Тут нужно отделять шаблон от специализации. Если даже специализация частичная, то должна определяться именно эта специализация, а вы тот же шаблон пытаетесь переопределять. А нужно определять FilterOnCircularVectors< Complex<T> >, следующим образом:

template <typename T>
class FilterOnCircularVectors<Complex<T>> 
      : public FilterAbstract<T, CircularVector<Complex<T>>>
{
public:
    /*Если классу нужны свои конструкторы, то нужно
    в списке инициализации конструктора инициализировать
    и базовый объект(он кажется даже не конструируется по 
     умолчанию)*/

    Complex<T> Step(const Complex<T>& value) override;

};
→ Ссылка
Автор решения: OnuchinVA

Как заметил @AR Hovsepyan задача решается, но дополнительно необходимо соблюсти правила наследования абстрактной функции. Тогда решение будет выглядеть следующим образом:

template <typename T>
class FilterOnCircularVectors<Complex<T>>
    : public FilterAbstract<T, CircularVector<Complex<T>>>
{
public:
    /*Если классу нужны свои конструкторы, то нужно
    в списке инициализации конструктора инициализировать
    и базовый объект(он кажется даже не конструируется по
     умолчанию)*/

    explicit FilterOnCircularVectors<Complex<T>>(uint32_t length);
    explicit FilterOnCircularVectors<Complex<T>>(const FilterAbstract<T, CircularVector<Complex<T>>> &object);
    Complex<T> Step(const Complex<T> &value);
private:
    T Step(const T &value) override;
};

Это связано с тем, что абстрактная функция наследуется с параметром T а для решения моей задачи требуется Complex<T>

→ Ссылка