Информация об изменениях

Сообщение Re: Приведение типов итераторов для контейнера с базовым тип от 12.01.2024 12:16

Изменено 12.01.2024 12:31 rg45

Re: Приведение типов итераторов для контейнера с базовым типом
Здравствуйте, drVanо, Вы писали:

V>Есть контейнер, который хранит указатели на объекты:

V> . . .

V>Есть некий базовый класс:

V>
V>class BaseImportList : public ObjectList<BaseImport>
V>{
V>...
V>}
V>


V>И есть потомок, который хранит в контейнере предка от BaseImport:

V>
V>class MacImport : public BaseImport
V>{
V>...
V>}

V>class MacImportList : public BaseImportList
V>{
V>    typedef typename std::vector<MacImport *> list;
V>    list::const_iterator begin() const { return reinterpret_cast<const list &>(items_).begin(); }
V>    list::const_iterator end() const { return reinterpret_cast<const list &>(items_).end(); }
V>}
V>


V>Вопрос, как вытащить "std::vector" из ObjectList для приведения типов итератора чтобы больше никогда не зависеть от типа контейнера в базовом классе?


К сожалению, в C++ нет ковариантности контейнеров и не предвидится в обозримом будущем. Но в данном случае в ней (в ковариантности) нет и ососбой нужды. Насколько я могу судить по дизайну, класс MacImportList сам по себе имеет семантику контейнера, а значит, нет особого смысла выставлять наружу инкапсулированый контейнер, которые является деталью реализации класса. Достаточно просто предоставить адаптор итератора, который будет брать на себя преобразование к нужному типу. Схематично так:

template <typename> struct BaseImportListIterator_;
template <typename T>  struct BaseImportListIterator_<T*> { using type = BaseImportList::iterator; };
template <typename T>  struct BaseImportListIterator_<T* const> { using type = BaseImportList::const_iterator; };

template <typename T>
using BaseImportListIterator = typename BaseImportListIterator_<T>::type;

template <typename T>
struct BaseImportListIteratorAdaptor : boost::iterator_adaptor<BaseImportListIteratorAdaptor<T>, BaseImportListIterator<T>, T>
{
   // 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 = BaseImportListIterator<T>;
   using BaseAdaptor = boost::iterator_adaptor<BaseImportListIteratorAdaptor, BaseIterator, T>;

   BaseImportListIteratorAdaptor() = default;
   BaseImportListIteratorAdaptor(const BaseIterator& i) : BaseAdaptor(i) {}

   T dereference() const { return static_cast<T>(*BaseAdaptor::base()); }
};

class MacImportList : public BaseImportList
{
public:
   using iterator = BaseImportListIteratorAdaptor<MacImport*>;
   using const_iterator = BaseImportListIteratorAdaptor<MacImport* const>;

   iterator begin() { return items_.begin(); }
   iterator end() { return items_.end(); }

   const_iterator begin() const { return items_.begin(); }
   const_iterator end() const { return items_.end(); }
};
Re: Приведение типов итераторов для контейнера с базовым тип
Здравствуйте, drVanо, Вы писали:

V>Есть контейнер, который хранит указатели на объекты:

V> . . .

V>Есть некий базовый класс:

V>
V>class BaseImportList : public ObjectList<BaseImport>
V>{
V>...
V>}
V>


V>И есть потомок, который хранит в контейнере предка от BaseImport:

V>
V>class MacImport : public BaseImport
V>{
V>...
V>}

V>class MacImportList : public BaseImportList
V>{
V>    typedef typename std::vector<MacImport *> list;
V>    list::const_iterator begin() const { return reinterpret_cast<const list &>(items_).begin(); }
V>    list::const_iterator end() const { return reinterpret_cast<const list &>(items_).end(); }
V>}
V>


V>Вопрос, как вытащить "std::vector" из ObjectList для приведения типов итератора чтобы больше никогда не зависеть от типа контейнера в базовом классе?


К сожалению, в C++ нет ковариантности контейнеров и не предвидится в обозримом будущем. Но в данном случае в ней (в ковариантности) нет и ососбой нужды. Насколько я могу судить по дизайну, класс MacImportList сам по себе имеет семантику контейнера, а значит, нет особого смысла выставлять наружу инкапсулированый контейнер, которые является деталью реализации класса. Достаточно просто предоставить адаптор итератора, который будет брать на себя преобразование к нужному типу. Схематично так:

template <typename> struct BaseImportListIterator_;
template <typename T>  struct BaseImportListIterator_<T*> { using type = BaseImportList::iterator; };
template <typename T>  struct BaseImportListIterator_<T* const> { using type = BaseImportList::const_iterator; };

template <typename T>
using BaseImportListIterator = typename BaseImportListIterator_<T>::type;

template <typename T>
struct BaseImportListIteratorAdaptor : boost::iterator_adaptor<BaseImportListIteratorAdaptor<T>, BaseImportListIterator<T>, T>
{
   // 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 = BaseImportListIterator<T>;
   using BaseAdaptor = boost::iterator_adaptor<BaseImportListIteratorAdaptor, BaseIterator, 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* const>;

   const_iterator begin() const { return items_.begin(); }
   const_iterator end() const { return items_.end(); }
};


Ну а формирование инкапсулированного контейнера и его модификация должны проиводиться какими-то специализированными функциями. Прямого доступа на модификацию икапсулированного контейнера давать не следует.