Проблема итератора C ++ и const_iterator для собственного класса контейнера - PullRequest
14 голосов
/ 16 мая 2010

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

Он состоит из контейнерного класса и двух тестовых классов: один тестовый класс, использующий 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. Что я делаю неправильно?

Ответы [ 3 ]

10 голосов
/ 16 мая 2010

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

Итератор STL содержит оператор приведения, который позволяет 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() { }
};
3 голосов
/ 16 мая 2010

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

2 голосов
/ 17 мая 2010

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

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

...