How can I use BOOST_FOREACH with a container supporting only const_iterator?
Asked Answered
C

4

8

I have this container:

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

Given that, the following code compiles just fine:

BOOST_FOREACH(Field field, row)
{
}

However, the Row class should not have the mutable iterator, so I changed the Row class, by removing the mutable access:

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

But now the same foreach loop fails to compile:

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>          ]
...

From the error message I understand that BOOST_FOREACH tries to instantiate a range_mutable_iterator type, which obviously fails. How do I make it to instantiate the constant range instead?

Thanks.

EDIT

Here is the complete class declarations for Row and 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;
};
Cereal answered 12/1, 2012 at 13:11 Comment(1)
Does BOOST_FOREACH(const Field& field, row) work?Procrustean
V
6

A work-around if you really want to avoid the iterator member is to use a pair of iterators.

BOOST_FOREACH(Field field, std::make_pair(row.begin(), row.end()))
Vaulting answered 12/1, 2012 at 13:18 Comment(1)
OK, it works. The only problem is that it is a bit too verbose, but nothing that cannot be fixed with a macro.Cereal
V
5

What was wrong with your original code?

Some of the standard library containers, like std::set and std::multiset, have iterators that are all const (no update allowed). The standard specifically says:

For associative containers where the value type is the same as the key type, both iterator and const_iterator are constant iterators. It is unspecified whether or not iterator and const_iterator are the same type.

You would probably get away with

typedef const_iterator iterator;

in your class.

Vantassel answered 12/1, 2012 at 13:16 Comment(4)
My container is not an associative one. Hence the passage does not apply to my code.Cereal
If you define your own container, you can define it anyway you want. I just wanted to point out that some standard containers have iterators that only have read-only access to the elements. Guess they work with Boost!Vantassel
I do not want to define the mutable versions of the begin() and end() methods either. Your advice is legitimate, but eliminating the mutable API altogether, if possible, is better than shortcircuiting it to the const API.Cereal
@Mark: Bo is quite right, you do not need to. Simply use the typedef, provide end and begin only as const and everything works fine, no mutable API leftAfghan
S
0

FieldIterator appears to be the same iterator class for const and non-const iterator methods. BOOST_FOREACH works with any container including C-style arrays which leads me to think the problem is in the FieldIterator class. Can you post the code for it?

Sustain answered 12/1, 2012 at 13:15 Comment(1)
Done - see the change in the body of the question.Cereal
L
0

With boost 1.52 (I have not tested with other versions), BOOST_FOREACH(Field field, const_cast<Row const&>(row)) will work too.

Lido answered 24/3, 2013 at 23:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.