Итератор над интерфейсом чистого виртуального контейнера в C ++ - PullRequest
0 голосов
/ 11 января 2019

У меня есть чисто виртуальный интерфейс для контейнера, который более или менее похож на это:

class IContainer
{
public:
    virtual ~IContainer() = default;
    virtual Element& operator[](size_t index) = 0;
    virtual const Element& operator[](size_t index) const = 0;
    virtual size_t size() const = 0;
};

Я хотел бы использовать диапазон для циклов, поэтому мне нужно определить begin () и end (). Для этого мне нужно также определить тип итератора.

Это не должно быть особенно сложно, но, тем не менее, я хотел бы знать, есть ли уже что-нибудь в STL или Boost, которое может помочь, прежде чем я начну кодировать то, что уже существует.

Ответы [ 2 ]

0 голосов
/ 11 января 2019

Возможно, это не очень хорошая идея, но добавить поддержку цикла for(:) здесь довольно просто. Я буду минимальным.

Я создам iteroid, не итератор, которого достаточно для поддержки циклов for(:). Для этого требуется поддержка ++, != и унарный *, и ничего больше.

template<class C>
struct index_iteroid {
  decltype(auto) operator*()const {
    return (*container)[i];
  }
  index_iteroid(index_iteroid const&)=default;
  index_iteroid& operator=(index_iteroid const&)=default;
  friend bool operator==(index_iteroid const& lhs, index_iteroid const& rhs) {
    return std::tie(lhs.i, lhs.container)==std::tie(rhs.i, rhs.container);
  }
  friend bool operator!=(index_iteroid const& lhs, index_iteroid const& rhs) {
    return !(lhs==rhs);
  }
  void operator++()&{
    ++i;
  }
  index_iteroid(C* c, std::size_t in):i(in), container(c) {}
private:
  std::size_t i = 0;
  C* container = nullptr;
};

теперь мы используем это:

class IContainer
{
public:
    virtual ~IContainer() = default;
    virtual Element& operator[](size_t index) = 0;
    virtual const Element& operator[](size_t index) const = 0;
    virtual size_t size() const = 0;
    index_iteroid<IContainer> begin() { return {this, 0}; }
    index_iteroid<IContainer> end() { return {this, size()}; }
    index_iteroid<IContainer const> begin() const { return {this, 0}; }
    index_iteroid<IContainer const> end() const { return {this, size()}; }
};

и вот оно у вас.

void test( IContainer* cont ) {
  if (!cont) return;
  for(Element& e : *cont) {
    // code
  }
}

прошу прощения за любые опечатки.

Теперь полный итератор занимает примерно в 2-3 раза больше кода, чем мой iteroid, но ничего сложного, в основном просто раздражающий шаблон.


Стандарт не особо вам поможет. Для повышения вы можете составить подсчитывающий итератор с помощью итератора / генератора, вызывающего функцию, и вызвать вызов функции []. В Boost также есть некоторые утилиты, позволяющие сократить время написания полного итератора, если вы хотите обновить итероид до итератора.

0 голосов
/ 11 января 2019

C ++ не делает такие "интерфейсы", как это. Идиоматический способ заключается в том, чтобы (потенциальные) клиенты IContainer вместо этого были шаблонизированы по типу контейнера и просто вызывали values[index], или были шаблонизированы по типу итератора и вызывали как *(first + offset).

В C ++ 20 вы сможете написать Container Концепцию , которая ведет себя как определение интерфейса, но вы уже можете выразить Концепцию как документированные требования. .

Если вместо этого вам нужен «контейнер» произвольного доступа со стертым типом, вы можете использовать boost::any_range<Element, boost::random_access_traversal_tag>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...