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

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

Изменено 12.01.2024 12:48 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 const_iterator = BaseImportListIteratorAdaptor<MacImport* const>;

   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 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(); }
};


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