Does `const_iterator` really need to be a differen

2019-07-18 17:23发布

问题:

Let say I define some kind of container A:

struct A
{
    iterator begin(){ return iterator(this,0); }
    const iterator cbegin() const { return iterator(this, last());}
    //...
};

Suppose now I want to declare the iterator (part of A):

struct A::iterator
{
    iterator ( A* ptr, size_t idx){};
    //...
};

Which I would use like:

const A a;
A::iterator it = a.cbegin();

That does not work because the pointer passed to the constructor of iterator is non-const.

The ideal solution would be something like a specific constructor that return a const object:

const A::iterator( const StringUtfInterface *p, size_t s); //Not valid

This is (obviously) not valid in C++. I wonder what is the approach to this problem?

Do I really need to declare/define a new const_iterator class? const keyword is not enough?


Related questions (but not the same):

  • Why does C++ not have a const constructor?
  • Get iterator for const reference

回答1:

const keyword is not enough?

Actually, const keyword is too much: it forces you to write

const A::iterator it = a.cbegin();

which prevents you from using ++it later on.

You need to provide two separate classes, but it does not mean that you have to write the code twice. You can structure an implementation of your iterator in such a way that a common class that does all the work is embedded in both constant and non-constant iterator implementations, which expose the relevant methods of the embedded implementation to the callers.



回答2:

What about just overloading the constructor of iterator to support constant container too?

struct A::iterator {
    iterator(A* ptr, size_t idx) {}
    iterator(const A* ptr, size_t idx) {}
    //...
};

In this way it is not necessary to define two separate classes and you will always obtain (implicitly) the right iterator depending on your container constness.

UPDATE

Following comment, you may use a template such as (not full implementation)

struct A {
  template<class T>
  struct base_iterator {
  private:
    T* _ptr;

  public:
    base_iterator(T* ptr, size_t idx) : _ptr(ptr) {}

    T operator*() { return *_ptr; }
    //...
  };

  typedef base_iterator<A> iterator;
  typedef base_iterator<const A> const_iterator;

  iterator begin() { return iterator(this, 0); }
  const_iterator cbegin() const { return const_iterator(this, 0); }
  //...
};