виртуальные методы, но и "виртуальный" тип, таким образом я бы мог его переопределить в производном классе?
Хочется сделать что-то вроде:
class IteratorBase {
public:
using Out; // <<< to be defined
virtual ~IteratorBase() { }
virtual Out next() = 0;
virtual unsigned long sizeHint() const = 0;
};
template<class Container>
class OwnIterator : public IteratorBase {
public:
using IterType = typename std::remove_const<typename Container::const_iterator>::type;
using Out = typename std::iterator_traits<typename Container::const_iterator>::value_type;
private:
Container container;
IterType iter;
public:
OwnIterator(Container c) : container(std::move(c)), iter(container.begin()) {}
Out next() { ... return *iter; }
...
// and so on
...
};
То есть в производном классе я определяю тип Out на нужный. Как подобные задачи решаются в C++?
CRTP например...
Можно параметризовать интерфейс этим типом, правда это будут разные интерфейсы (на каждый Out - свой): template <class Out> class IteratorBase
Ну точнее просто передать как шаблонный параметр, да
Тогда никак. Функция возвращает фиксированный тип. Виртуальная не может быть шаблоном. Значит нужно параметризовать класс.
а std::any не сгодится?
Как вы предполагаете возвращать разные типы из функции, которая выбирается уже в рантайме? IteratorBase* base = /* ... */; auto next = base->next(); ^^^^ должен быть известен компилятору в компилтайме Что должен компилятор подставить на место auto? Угадать намерения программиста? А если программист хочет невозможного?
Возможно.
Я могу ограничить все Out-ы ограничить типами с одинаковым или ограниченным размером или, напротив, попросить пожаловаться компилятор, если у него не получается нужный тип вывести
Компилятор почти никогда не может вывести тип. Потому что наследник IteratorBase расположен в dll. Или скрыт в ядре ОС. А быть может, находится в памяти компьютера на тёмной стороне Луны и все запросы к нему выполняются по некоторому RPC Вы, вероятно, имеете ввиду, что у возвращаемого значения есть некоторый интерфейс? Правильно я понимаю, что вам кажется, что вы хотите type erasure на основе этого интерфейса?
Более-менее близкий вариант: объявить внутри базового интерфейсе ещё один struct IValue, возвращать std::unique_ptr<IValue> из next(). В наследниках реализовывать интерфейс. При желании это оптимизируется.
Согласен, с тем недостатком, однако, что на unsigned long интерфейс не навесишь. Придётся похоже в next возвращать сырой указатель и на стороне клиента кастовать.
Не совсем. Можно воспользоваться стиранием типа и в интерфейс завернуть вообще что угодно. Например, в наследнике будет struct Value : IValue { Out value; ... }
Спасибо за детальное разъяснение. Данный приёмчик с типом-мембером довольно широко используется в Скале https://github.com/xmanu/scala-virtual-classes-annotation-macros У меня была гипотеза, что аналог есть и в плюсах
О, вот это любопытно, спасибо.
Обсуждают сегодня