Итератор С++ и проблема const_iterator для собственного класса контейнера

Я пишу собственный контейнерный класс и столкнулся с проблемой, с которой я не могу разглядеть. Здесь показан образец пустой кости, который показывает проблему.

Он состоит из класса контейнера и двух тестовых классов: один тестовый класс с использованием std: vector, который компилируется красиво, и второй тестовый класс, который пытается использовать мой собственный класс контейнера точно таким же образом, но с трудом скомпилируется.

#include <vector>
#include <algorithm>
#include <iterator>

using namespace std;

template <typename T>
class MyContainer
{
public:

  class iterator
  {
  public:
    typedef iterator self_type;
    inline iterator() { }
  };

  class const_iterator
  {
  public:
    typedef const_iterator self_type;
    inline const_iterator() { }
  };

  iterator begin() {
    return iterator();
  }

  const_iterator begin() const {
    return const_iterator();
  }
};

// This one compiles ok, using std::vector
class TestClassVector
{
public:
  void test() {
    vector<int>::const_iterator I=myc.begin();
  }

private:
  vector<int> myc;
};

// this one fails to compile. Why?
class TestClassMyContainer
{
public:
  void test(){
    MyContainer<int>::const_iterator I=myc.begin();
  }

private:
  MyContainer<int> myc;
};


int main(int argc, char ** argv)
{
  return 0;
}

gcc говорит мне:

test2.C: В функции-члена 'void TestClassMyContainer:: test():

test2.C: 51: ошибка: преобразование из 'MyContainer:: iterator в нескалярный тип' MyContainer:: const_iterator запрошен

Я не уверен, где и почему компилятор хочет преобразовать итератор в const_iterator для моего собственного класса, но не для векторного класса STL. Что я делаю не так?

Ответ 1

Когда вы вызываете begin(), компилятор по умолчанию создает вызов не-const begin(). Поскольку myc не const, он не знает, что вы хотите использовать const begin(), а не не const begin().

Итератор STL содержит оператор литья, который позволяет без iterator преобразовать iterator в const_iterator. Если вы хотите, чтобы это сработало, вам нужно добавить его так же:

class iterator
{
public:
    typedef iterator self_type;
    inline iterator() { }

    operator const_iterator() { return const_iterator(); }
};

или разрешить const_iterator быть построено из iterator следующим образом:

class const_iterator
{
public:
    typedef const_iterator self_type;

    const_iterator(iterator& ) {}
    inline const_iterator() { }
};

Ответ 2

В контейнерах тип iterator должен быть конвертирован в const_iterator. Это необходимо для случаев, когда вы выполняете итерацию через изменяемый контейнер с использованием итератора non-mutable (const), поскольку это имеет смысл. В вашем случае myc является изменяемым (не const), но вы создаете на нем итератор const.

Ответ 3

Вам следует посмотреть библиотеку Boost.Iterators, особенно разделы iterator_facade и iterator_adaptor. Они содержат накопление итератора "с нуля".

Он покажет вам, как писать итераторы без чрезмерного дублирования, потому что в большинстве случаев код const и non-const версии примерно одинаковый, кроме самой квалификации const. Используя шаблоны, можно написать один раз, затем объявить два разных типа и что иллюстрирует документация библиотеки.