Как я могу использовать BOOST_FOREACH с контейнером, поддерживающим только const_iterator?
У меня есть этот контейнер:
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
Тип, который, очевидно, не удается. Как мне сделать так, чтобы вместо этого создать экземпляр постоянного диапазона?
Благодарю.
РЕДАКТИРОВАТЬ
Вот полные объявления класса для 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 ответа
Обходной путь, если вы действительно хотите избежать iterator
Член должен использовать пару итераторов.
BOOST_FOREACH(Field field, std::make_pair(row.begin(), row.end()))
Что не так с вашим исходным кодом?
Некоторые из стандартных контейнеров библиотеки, такие как std::set
а также std::multiset
, иметь итераторы, которые все являются константами (обновление не допускается). Стандарт конкретно гласит:
Для ассоциативных контейнеров, в которых тип значения совпадает с типом ключа, итератор и const_iterator являются постоянными итераторами. Не определено, являются ли итераторы и const_iterator одинаковыми.
Вы, вероятно, сойдете с
typedef const_iterator iterator;
в вашем классе.
С бустом 1.52 (я не тестировал с другими версиями), BOOST_FOREACH(Field field, const_cast<Row const&>(row))
тоже будет работать
FieldIterator
представляется одинаковым классом итераторов для методов const и неконстантных итераторов. BOOST_FOREACH
работает с любым контейнером, включая массивы в стиле C, что заставляет меня думать, что проблема в FieldIterator
учебный класс. Можете ли вы опубликовать код для этого?