C ++: перегрузка методов list.end () и list.begin () для постоянных итераторов - PullRequest
4 голосов
/ 18 декабря 2010

Я все еще пытаюсь реализовать свою собственную версию класса LinkedList, и теперь у меня проблемы с перегрузкой методов для постоянных итераторов. Например, когда я пытаюсь распечатать список, используя этот код:

cout << "citer:" << endl;
for (UberList<int>::CIter it = ulist.begin(); it != ulist.end(); ++it)
{
 cout << *it << " ";
}
cout << endl;

У меня есть эти ошибки:

Error E2034 UberList2.cpp 532: Cannot convert 'UberList<int>::Iter' to 'UberList<int>::CIter' in function main()
Error E2094 UberList2.cpp 532: 'operator!=' not implemented in type 'UberList<int>::CIter' for arguments of type 'UberList<int>::Iter' in function main()

Насколько я понял, это означает, что используются те обычные методы итераторов конца и начала. Вот как эти методы объявлены в моем классе:

Iter begin();
Iter end();
CIter begin() const;
CIter end() const;

и

template<class T>
typename UberList<T>::Iter UberList<T>::begin()
{
    Iter it;
    it.curr = head;
    return it;
}

template<class T>
typename UberList<T>::Iter UberList<T>::end()
{
 Iter it;
 it.curr = tail->next;
 return it;
}

template<class T>
typename UberList<T>::CIter UberList<T>::begin() const
{
 CIter it;
 it.ccurr = head;
 return it;
}

template<class T>
typename UberList<T>::CIter UberList<T>::end() const
{
 CIter it;
 it.ccurr = tail->next;
 return it;
}

Есть ли способ заставить мою программу использовать эти методы const для постоянных итераторов вместо обычных? Я был бы рад услышать любой совет.

Да, и вот код моего класса в одном файле на всякий случай: http://pastebin.com/Jbvv5Hht

Ответы [ 5 ]

7 голосов
/ 18 декабря 2010

Вы должны предоставить преобразование из Iter в CIter.Стандартные контейнеры делают (Таблица 65, в 23.1 «Требования к контейнерам», говорит, что X::iterator преобразуется в X::const_iterator)

Вызывающий может гарантировать, что перегрузка const вызывается с помощью constсправка, но вы не должны заставлять их делать это, потому что они должны будут написать что-то вроде:

UberList<int>::CIter it = static_cast<const UberList<int> &>(ulist).begin()

Если вы предоставите «требуемое» преобразование, тогда вашему вызывающему абоненту не нужно делать что-то особенное: ваш оригинальный код будет работать так же, как и для стандартных контейнеров.

1 голос
/ 19 декабря 2010

Вздох: здесь есть уровень хакерства, разработанный для того, чтобы скрыть тот факт, что концептуально то, что вы хотите сделать, не может быть сделано автоматически в C ++, потому что оно не понимает дисперсию. Некоторые другие языки (включая Ocaml) делают.

Если у вас есть функтор (это шаблонный класс для программистов на C ++), вопрос заключается в том, как он и различные функции ведут себя с дисперсией параметра, например при преобразовании из T в T const. То, что вы действительно хотите, это:

List<T> --> List<T const>

другими словами, вы хотите, чтобы функтор List был ковариантным. Но нет, это не так ... на самом деле шаблон List вообще не является функтором, потому что функторы должны сохранять структуру, а преобразование не отражается должным образом. В свою очередь это означает, что либо шаблоны C ++ сломаны, либо концепция const нарушена, потому что система типов, которая не поддерживает параметрический полиморфизм, нарушается спецификацией:)

Предоставление const_iterator не решает эту проблему, оно просто исправляет разрыв. Где версия volatile и const_volatile? Как насчет двойных косвенных указаний?

Если вы не понимаете двойной косвенности: рассмотрите дерево векторов T, это два шаблона:

Tree<Vector<T>>

Лучшее решение здесь - отказаться от поддержки const_iterator. Просто не беспокойся. В любом случае это сбивает с толку: как насчет "const vector"? Что это такое? Вектор, который вы не можете сделать длиннее, но он по-прежнему позволяет писать элементы?

Фактическое требование заключается в том, чтобы преобразовать коммутируют, например:

vector<T> const == vector<T const>

[или они не коммутируют, если преобразование противоречиво]

Тот факт, что этого не происходит, показывает, что вектор не является функторным, другими словами, шаблоны не могут эффективно использоваться для параметрического полиморфизма. Если вы действительно хотите связать свои узлы в узел, рассмотрите шаблоны с аргументами функции и спросите о дисперсии возвращаемого типа и параметров функции и о том, как это может повлиять на контейнер. Хороший пример - как составить две функции, чтобы они работали над парой. Что, если они мутаторы, как тогда работает "const"?

1 голос
/ 18 декабря 2010

Еще несколько комментариев к коду ОП.Попробуйте разделить этот гигантский класс uberl33tlist и разбить его на более мелкие файлы.Видеть, как все эти друзья объявляют класс, делает меня довольно неловко.Существуют также некоторые хитрые семантики. Когда вы используете такие вещи, как

friend class UberList;
friend class CIter;

В некоторых случаях эти операторы также заканчиваются предварительным объявлением этих классов, если они еще не существуют.Здесь также есть что-то не совсем правильное, когда вы смотрите на ваш оператор присваивания:

UberList<T> operator = (const UberList<T>& OL)
{
    UberList<T> NL = new (OL);
    return NL;
}

Также у вас есть основной

it2++;
ulist.insertAfter(b, it2);
//...

Вы используете постфиксный оператор ++ для it2, но нереализовать твой класс итератора.Borland принимает его, используя вместо этого префикс, но выдает предупреждение.Gcc помечает это как ошибку и отклоняет код.Возможно, захочется разобраться в этом

0 голосов
/ 18 декабря 2010

Используется обычный метод begin, потому что переменная не const.Таким образом, один из способов исправить это сделать другую (справочную) переменную, которая является константой:

UberList<int> const & culist = ulist;
for (UberList<int>::Citer it = culist.begin(); ...)

В качестве альтернативы используйте const_cast.

0 голосов
/ 18 декабря 2010

Вам нужно

teamplate<class T> bool operator!=(UberList<T>::CIter,UberList<T>::CIter);
...