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;
};
mark
  • 59,016
  • 79
  • 296
  • 580

4 Answers4

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()))
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • OK, it works. The only problem is that it is a bit too verbose, but nothing that cannot be fixed with a macro. – mark Jan 12 '12 at 19:26
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.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • My container is not an associative one. Hence the passage does not apply to my code. – mark Jan 12 '12 at 13:47
  • 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! – Bo Persson Jan 12 '12 at 13:52
  • 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. – mark Jan 12 '12 at 15:47
  • @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 left – Martin Mar 30 '12 at 16:23
0

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

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?

Mark Ingram
  • 71,849
  • 51
  • 176
  • 230