Здравствуйте, drVanо, Вы писали:
V>Вопрос, как вытащить "std::vector" из ObjectList для приведения типов итератора чтобы больше никогда не зависеть от типа контейнера в базовом классе?
Самое простое — оставить шаблон в BaseImportList. Иначе std::views::transform или transform_iterator из буста, или какой-нибудь свой cast_iterator.
Re[2]: Приведение типов итераторов для контейнера с базовым
Здравствуйте, andrey.desman, Вы писали:
AD>Самое простое — оставить шаблон в BaseImportList. Иначе std::views::transform или transform_iterator из буста, или какой-нибудь свой cast_iterator.
BaseImportList не может быть шаблонным, т.к. через работает код, который знает только про BaseImport.
Здравствуйте, drVanо, Вы писали:
V>BaseImportList не может быть шаблонным, т.к. через работает код, который знает только про BaseImport.
Это называется "дженерики для бедных"
Я бы подумал в такую сторону:
— нешаблонный базовый контейнер с vector<void*>
— шаблон типизированной обёртки доступа к нему
— шаблон типизированной обёртки хранения его
— операции приведения
Если упереться, то можно разрешить неявный кастинг вверх для константных контейнеров и ссылок.
То есть, константный контейнер наследников — это субкласс константного контейнера предков. (По принципу подстановки Лисков).
А вот кастинг вниз — тут уже будет веселье...
Перекуём баги на фичи!
Re: Приведение типов итераторов для контейнера с базовым тип
V>Вопрос, как вытащить "std::vector" из ObjectList для приведения типов итератора чтобы больше никогда не зависеть от типа контейнера в базовом классе?
К сожалению, в C++ нет ковариантности контейнеров и не предвидится в обозримом будущем. Но в данном случае в ней (в ковариантности) нет и ососбой нужды. Насколько я могу судить по дизайну, класс MacImportList сам по себе имеет семантику контейнера, а значит, нет особого смысла выставлять наружу инкапсулированый контейнер, которые является деталью реализации класса. Достаточно просто предоставить адаптор итератора, который будет брать на себя преобразование к нужному типу. Схематично так:
template <typename T>
struct BaseImportListIteratorAdaptor : boost::iterator_adaptor<
BaseImportListIteratorAdaptor<T>, // This class
BaseImportList::const_iterator, // Base iterator
T*, // Value type
boost::use_default, // Category of traversal
T* // Reference (not allowing real references to pointers)
>
{
// Use concepts instead of the static_assert on C++20
static_assert(std::is_base_of_v<BaseImport, std::remove_pointer_t<std::decay_t<T>>>);
using BaseIterator = BaseImportList::const_iterator;
using BaseAdaptor = boost::iterator_adaptor<
BaseImportListIteratorAdaptor,
BaseImportList::const_iterator,
T*,
boost::use_default,
T*
>;
BaseImportListIteratorAdaptor() = default;
BaseImportListIteratorAdaptor(const BaseIterator& i) : BaseAdaptor(i) {}
T* dereference() const { return static_cast<T*>(*BaseAdaptor::base()); }
};
class MacImportList : public BaseImportList
{
public:
using const_iterator = BaseImportListIteratorAdaptor<MacImport>;
using iterator = const_iterator;
const_iterator begin() const { return items_.begin(); }
const_iterator end() const { return items_.end(); }
};
Ну а формирование инкапсулированного контейнера и его модификация должны проиводиться какими-то специализированными функциями. Прямого доступа на модификацию икапсулированного контейнера давать не следует.
--
Не можешь достичь желаемого — пожелай достигнутого.
R>>class MacImportList : public BaseImportList R>>{ R>>public: R>> using const_iterator = BaseImportListIteratorAdaptor<MacImport>; R>> using iterator = const_iterator;
R>> const_iterator begin() const { return items_.begin(); } R>> const_iterator end() const { return items_.end(); } R>>}; R>>[/cpp]
V>Я правильно понимаю, что вы забыли приведение типов вокруг "items_.begin()" и "items_.end()"?
Нет, неправильно. Предоставленный выше адатпор предоставляет соответствующий неявный конструктор, по итератору базового типа, поэтому явного преобразования не требуется:
Просто чем хорош boost::iterator_adaptor — он определяет полный джентльменский набор всех атрибутов итератора — методы, типы, категорию и пр.
Да, еще один момент: адапторы итераторов, на самом деле, классная штука, с помощью которой можно адаптировать разного рода коллекции под конкретные нужды. Например, ты можешь подпилить этот адаптор так, чтоб он возвращал не указатели, а сразу ссылки на объекты. Снаружи MacImportList будет выглядеть как обычный контейнер объектов класса MacImport.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Да, еще один момент: адапторы итераторов, на самом деле, классная штука, с помощью которой можно адаптировать разного рода коллекции под конкретные нужды. Например, ты можешь подпилить этот адаптор так, чтоб он возвращал не указатели, а сразу ссылки на объекты. Снаружи это будет выглядеть как вектор объектов класса MacImport.
Класс! Засунул адаптор внутрь ObjectList, теперь "снаружи" нужен только один тип для адаптора для всех потомков.
Здравствуйте, rg45, Вы писали:
R>Да, еще один момент: адапторы итераторов, на самом деле, классная штука, с помощью которой можно адаптировать разного рода коллекции под конкретные нужды. Например, ты можешь подпилить этот адаптор так, чтоб он возвращал не указатели, а сразу ссылки на объекты.
Можно какой-нибудь простой пример такого адаптора к std::vector<std::unique_ptr<T>>?
Re[7]: Приведение типов итераторов для контейнера с базовым
Здравствуйте, drVanо, Вы писали:
R>>Да, еще один момент: адапторы итераторов, на самом деле, классная штука, с помощью которой можно адаптировать разного рода коллекции под конкретные нужды. Например, ты можешь подпилить этот адаптор так, чтоб он возвращал не указатели, а сразу ссылки на объекты.
V>Можно какой-нибудь простой пример такого адаптора к std::vector<std::unique_ptr<T>>?
Ну это уже зависит от потребностей — смотря, что нужно получить на выходе. Вообще, этих всяких адаптеров можно напридумывать вагон и маленькую тележку (что собственно уже и сделано в boost). И имеет смысл вместо сложных монолитных адаптеров делать маленькие простые шаблончики, а потом их комбинировать как конструктор лего.
В примере ниже определены два простых адаптора: IndirectIterator и StaticCastIterator. Эти два адаптора комбинируются (StaticCastIterator поверх IndirectIterator) и применяются к инкапсулированному контейнеру std::vector<std::unique_ptr<T>>. В принципе, вместо unique_ptr можно подставить любой другой вид указателя и будет работать точно так же. В примере присутствует избыточный копи-пастинг — это ради того, чтоб не перегружать пример дополнительными сущностями и не подключать boost. По-хорошему, такого быть не должно, конечно же (для этого и придуманы шаблоны boost::iterator_facade и boost::iterator_adaptor).
Здравствуйте, drVanо, Вы писали:
V>Можно какой-нибудь простой пример такого адаптора к std::vector<std::unique_ptr<T>>?
В дополнение к вышесказанному. Адаптеры можно писать не только для итераторов, но и для целых контейнеров. Можно, например, написать обобщенный шаблонный адаптор для std::vector и поддержать в нем ковариантность (типа CovariantVector). Тогда каждый такой контейнер будет автоматом являться и контейнером элементов базовых классов. Можно пойди еще дальше и сделать еще более обобщенный шаблон, который можно будет использовать не только с вектором, но и с другими контейнерами, типа того как это сделано в std::queue и std::stack. Правда, здесь возникают некоторые вопросы с принципом Лисков, возможно, именно поэтому такие адапторы отсутствуют в стандартной библиотеке, но, как частное решение для собственных нужд, это может иметь право на существование.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>В дополнение к вышесказанному. Адаптеры можно писать не только для итераторов, но и для целых контейнеров. Можно, например, написать обобщенный шаблонный адаптор для std::vector и поддержать в нем ковариантность (типа CovariantVector). Тогда каждый такой контейнер будет автоматом являться и контейнером элементов базовых классов. Можно пойди еще дальше и сделать еще более обобщенный шаблон, который можно будет использовать не только с вектором, но и с другими контейнерами, типа того как это сделано в std::queue и std::stack.
Мы можем пообщаться через telegram?
P.S. Написал в личку — не знаю дойдет или нет.
Re[9]: Приведение типов итераторов для контейнера с базовым
Здравствуйте, drVanо, Вы писали:
V>Я хотел пообщаться на темы, которые выходят за рамки этого форума, хотя напрямую связаны с C++. V>Мой телеграм: https://t.me/vmpsoft
Пока нет возможности, к сожалению.
--
Не можешь достичь желаемого — пожелай достигнутого.