Как я могу использовать BOOST_FOREACH с контейнером, поддерживающим только const_iterator? - PullRequest
8 голосов
/ 12 января 2012

У меня есть этот контейнер:

class /*final*/ Row
{
public:
  typedef FieldIterator const_iterator;
  typedef FieldIterator iterator;
  FieldIterator begin() const;
  FieldIterator end() const;
  FieldIterator begin();
  FieldIterator end();
  ...
};

Учитывая это, следующий код компилируется просто отлично:

BOOST_FOREACH(Field field, row)
{
}

Однако класс Row не должен иметь изменяемый итератор, поэтому я изменил класс Row, удалив изменяемый доступ:

class /*final*/ Row
{
public:
  typedef FieldIterator const_iterator;
  FieldIterator begin() const;
  FieldIterator end() const;
  ...
};

Но теперь тот же цикл foreach не компилируется:

1>o:\c\boost_1_48_0\boost\foreach.hpp(364): error C2039: 'type' : is not a member of 'boost::mpl::eval_if<C,F1,F2>'
1>          with
1>          [
1>              C=boost::mpl::false_,
1>              F1=boost::range_const_iterator<sqlserver::Row>,
1>              F2=boost::range_mutable_iterator<sqlserver::Row>
1>          ]
1>          c:\dev\internal\playmssqlce\playmssqlce.cpp(29) : see reference to class template instantiation 'boost::foreach_detail_::foreach_iterator<T,C>' being compiled
1>          with
1>          [
1>              T=sqlserver::Row,
1>              C=boost::mpl::false_
1>          ]
...

Из сообщения об ошибке я понимаю, что BOOST_FOREACH пытается создать экземпляр типа range_mutable_iterator, который явно терпит неудачу. Как мне сделать так, чтобы вместо этого создать экземпляр постоянного диапазона?

Спасибо.

EDIT

Вот полные объявления классов для Row и FieldIterator:

class /*final*/ Row
{
  const BYTE *m_buffer;
  const DBBINDING *m_pColumnBindings;
  int m_columnBindingCount;
  FieldIterator m_end;
public:
  typedef FieldIterator const_iterator;
  typedef FieldIterator iterator;
  Row(const BYTE *buffer, const DBBINDING *pColumnBindings, int columnBindingCount);
  bool isSameRow(const Row& r) const;
  int fieldCount() const;
  Field field(int i) const;
  Field& field(int i, void *fieldBuffer) const;
  FieldIterator begin() const;
  FieldIterator end() const;
  FieldIterator begin();
  FieldIterator end();
};

class FieldIterator : public iterator_facade<FieldIterator, Field, boost::random_access_traversal_tag>
{
  const Row *m_pRow;
  int m_index;
  mutable BYTE m_fieldBuffer[sizeof(Field)];
public:
  FieldIterator(const Row *pRow = NULL, int index = 0);
private:
  friend class boost::iterator_core_access;
  void increment();
  void decrement();
  void advance(difference_type n);
  difference_type distance_to(FieldIterator it);
  reference dereference() const;
  bool equal(const FieldIterator& rhs) const;
};

Ответы [ 4 ]

6 голосов
/ 12 января 2012

A Обходной путь Если вы действительно хотите избежать членства iterator, используйте пару итераторов.

BOOST_FOREACH(Field field, std::make_pair(row.begin(), row.end()))
5 голосов
/ 12 января 2012

Что не так с вашим исходным кодом?

Некоторые из стандартных контейнеров библиотеки, такие как std::set и std::multiset, имеют итераторы, которые являются постоянными (обновление не допускается).Стандарт, в частности, гласит:

Для ассоциативных контейнеров, в которых тип значения совпадает с типом ключа, итератор, и const_iterator являются постоянными итераторами.Не определено, являются ли итераторы и const_iterator одним и тем же типом.

Возможно, вам не подойдет

typedef const_iterator iterator;

в вашем классе.

0 голосов
/ 25 марта 2013

С бустом 1,52 (я не тестировал другие версии), BOOST_FOREACH(Field field, const_cast<Row const&>(row)) тоже будет работать.

0 голосов
/ 12 января 2012

FieldIterator представляется одним и тем же классом итераторов для константных и неконстантных методов итераторов.BOOST_FOREACH работает с любым контейнером, включая массивы в стиле C, что наводит меня на мысль, что проблема в классе FieldIterator.Можете выложить код для этого?

...