Приведение параметров вложенных шаблонов в интеллектуальный указатель C ++ - PullRequest
0 голосов
/ 04 апреля 2020

Рассмотрим следующий минимальный (но полный) пример C ++ 17:

#include <iostream>
#include <memory>

class Parent {
public:
    Parent() = default;
};

class Child : public Parent {
public:
    Child() : Parent() {};
};

template <class T> class Sink;

template <class T> class Source {
    template <class O> friend class Sink;

public:
    Source() = default;

    void operator>=(std::shared_ptr<Sink<T>> sink) {
        m_sink = sink;
        sink->m_source = this;
    }

    template <class MORPH>
    typename std::enable_if_t<std::is_base_of<Sink<T>, MORPH>::value, void>
    operator>=(std::shared_ptr<MORPH> sink) {
        return *this >= std::static_pointer_cast<Sink<T>>(sink);
    }

private:
    std::shared_ptr<Sink<T>> m_sink;
};

template <class T> class Sink {
    template <class O> friend class Source;

public:
    Sink() = default;

private:
    Source<T> *m_source = nullptr;
};

int main(int argc, char *argv[]) {
    Source<Child> source;
    std::shared_ptr<Sink<Parent>> sink;

    source >= sink;

    return 0;
}

Я пытаюсь сделать здесь основную c архитектуру канала и фильтра.

Существуют типы Source и Sink (для простоты нет каналов), и они связаны через оператор >=.

При создании источника и приемника, которые специализируются на одном типе, все Это хорошо. Но когда источник генерирует объекты типов, которые наследуются от базового класса (как показано в примере), и приемник принимает такие родительские типы, вещи взрываются.

Короче говоря, будет работать следующее:

Source<Child> source;
std::shared_ptr<Sink<Child>> sink;

но я хотел бы принять расширенный набор типов в приемнике.

Кроме того, я хочу иметь возможность связывать типы, также наследуемые от Sink, таким образом, шаблон MORPH Требуется перегрузка оператора. Сейчас это прекрасно работает для меня (до тех пор, пока используется тот же параметр T).

Пока я придумал грязный хак:

    template <class T_>
    typename std::enable_if_t<std::is_base_of<T_, T>::value, void>
    operator>=(std::shared_ptr<Sink<T_>> sink) {
        m_sink = std::shared_ptr<Sink<T>>(reinterpret_cast<Sink<T>*>(sink.get()));
        sink->m_source = reinterpret_cast<Source<T_>*>(this);
    }
...
    template <class T_, template <class> class MORPH>
    typename std::enable_if_t<std::is_base_of<T_, T>::value && std::is_base_of<Sink<T_>, MORPH<T_>>::value, void>
    operator>=(std::shared_ptr<MORPH<T_>> sink) {
        return *this >= std::static_pointer_cast<Sink<T_>>(sink);
    }

Это меня утомляет по двум причинам:

  1. Необходимо заново создать shared_ptr, чтобы иметь возможность назначить его m_sink
  2. Все reinterpret_cast в необработанных указателях в соответствии с шаблоном типы T и T_

Есть ли лучший способ достичь sh того, к чему я стремлюсь?

...