Я пишу собственный контейнерный класс и столкнулся с проблемой, я не могу получить голову вокруг. Вот базовый образец, который показывает проблему.
Это состоит из контейнерного класса и двух тестовых классов: один тестовый класс с помощью 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: В функции членства ‘освобождают TestClassMyContainer:: тест ()’:
test2. C:51: ошибка: преобразование из ‘MyContainer:: итератор’ к нескалярному типу ‘MyContainer:: const_iterator’ требуют
Я не уверен, где и почему компилятор хочет преобразовать итератор в const_iterator для моего собственного класса, но не для класса вектора STL. Что я делаю неправильно?
Когда вы вызываете begin ()
, компилятор по умолчанию создает вызов неконстантной функции begin ()
. Поскольку myc
не является const, он не может узнать, что вы собираетесь использовать const begin ()
, а не неконстантную begin ()
.
Итератор STL содержит оператор приведения, который позволяет незаметно преобразовать итератор
в const_iterator
. Если вы хотите, чтобы это работало, вам нужно добавить еще один такой же пример:
class iterator
{
public:
typedef iterator self_type;
inline iterator() { }
operator const_iterator() { return const_iterator(); }
};
или разрешить создание const_iterator
из итератора
следующим образом:
class const_iterator
{
public:
typedef const_iterator self_type;
const_iterator(iterator& ) {}
inline const_iterator() { }
};
В контейнерах тип итератора
должен быть преобразован в const_iterator
. Это необходимо в тех случаях, когда вы перебираете изменяемый контейнер с использованием неизменяемого (константного) итератора, поскольку это имеет смысл. В вашем случае myc
является изменяемым (неконстантным), но вы создаете для него константный итератор.
Вам следует взглянуть на библиотеку Boost.Iterators, особенно на разделы iterator_facade
и iterator_adaptor
. Они содержат построение итератора «с нуля».
Он покажет вам, как писать итераторы без особого дублирования, потому что в большинстве случаев код константной и неконстантной версий примерно одинаков, за исключением самой квалификации const
. Используя шаблоны, можно написать его один раз, а затем объявить два разных типа, и это иллюстрирует документация библиотеки.