Преобразовать объект класса итератора на основе пользовательского шаблона в const_iterator

Я изучаю курс ООП (C ++ — базовый язык) в университете. Моя задача — реализовать собственный контейнерный шаблон класса связанного списка. Я сделал это почти полностью, но столкнулся с проблемой. Известно, что STL обеспечивает iterator а также const_iterator классы для итерации по списку. Они имеют почти одинаковую реализацию, главное отличие в том, что методы итератора возвращают ссылки, а методы const_iterator — константы. Я последовал за https://stackoverflow.com/a/3582733/2108548 и создал отдельный класс шаблона ListIterator, Тогда я объявил с typedef классы Iterator а также ConstIterator внутри класса List,

Я получил что-то вроде этого:

template<typename T>
class ListNode
{
public:
ListNode(T *node_value = nullptr, ListNode *node_prev = nullptr, ListNode *node_next = nullptr):
value(node_value), prev(node_prev), next(node_next) { }

T *value;
ListNode *prev, *next;
};

template<typename T>
class ListIterator
{
typedef ListNode<T> Node;

public:
ListIterator();
ListIterator(Node *node);
ListIterator(ListIterator const &other);
ListIterator &operator++();
// ...
Node *i;
};

template<typename T>
class List: public Container
{
typedef ListIterator<T> Iterator;
typedef ListIterator<T const> ConstIterator;
// ...
Iterator begin() const
{
return Iterator(m_first->next);
}
ConstIterator const_begin() const
{
return ConstIterator(begin());
}
// ...
};

Все работало отлично, пока я не решил сделать «копи-конструктор» Iterator -> ConstIterator, Поэтому мне нужен метод конструктора, который получает ListIterator<T> (где T имя класса данных) и создает новый тип объекта ListIterator<T const>, Но по факту ConstIteratorконструктор получит T const в качестве параметра шаблона, поэтому мне нужно удалить const для параметра конструктора. Я нашел заголовок type_traits который делает это. Итак, я написал «copy-constructor»:
typedef имя типа std :: remove_cv :: type NoConstT;
ListIterator (ListIterator const &Другой);

Но это не работает! Я получил эту ошибку после запроса const_begin ():

List<int> list1;
list1 << 1 << 2 << 3;
int i = *list1.const_begin();

error: 'ListIterator<T>::ListIterator(const ListIterator<typename std::remove_cv<_Tp>::type>&) [with T = int; typename std::remove_cv<_Tp>::type = int]' cannot be overloaded with 'ListIterator<T>::ListIterator(const ListIterator<T>&) [with T = int; ListIterator<T> = ListIterator<int>]'

Но это не все. Для достижения моей цели нужно преобразовать ListNode<T> в ListNode<T const> также. Но у меня есть еще одна проблема: каждый узел списка содержит указатели на предыдущий и следующий узлы, и если я попытаюсь инициализировать их в конструкторе узла, я получу рекурсию. Конечно, я могу создать функцию, которая обрабатывает все преобразования ListNode<T> узлы для ListNode<T const> итерацией по ним. Но мне не нравится это решение: оно имеет огромные накладные расходы!

Я задал этот вопрос моему учителю. Он не мог понять это в течение нескольких минут, затем, когда он получил это, он сказал: «Это элементарно!» — «Но я застрял с этим на 3-4 часа!» — «Если так, отбросьте const-итераторы и завершите контейнер списка без них. Мне нужно время, чтобы понять ваш код» (как вы видите, мой код довольно прост, на мой взгляд). Как я понял, он не знал ответа на этот вопрос. Но я очень хочу знать, как это сделать! Как я могу решить эту проблему?

Извините за массу ошибок — я не являюсь носителем английского языка.

4

Решение

Вы действительно можете использовать <type_traits>Просто не так, как вы описываете. Один из подходов состоит в том, чтобы всегда объявлять конструктор одного и того же типа и объявлять неконстантный условно с enable_if только когда аргумент шаблона действительно не является постоянным. И узел всегда должен быть неконстантным, это вы можете сделать с remove_const,

#include <type_traits>

template<typename T>
class ListNode
{
//...
};

template<typename T>
class ListIterator
{
typedef ListNode<typename std::remove_const<T>::type> Node;

public:
ListIterator() {}
ListIterator(Node*) {}
ListIterator(ListIterator const&) {}
template <typename U>
ListIterator(ListIterator<U> const&, typename std::enable_if<!std::is_const<U>()>::type* = nullptr) {}
};

template<typename T>
class List
{
public:
typedef ListIterator<T> Iterator;
typedef ListIterator<T const> ConstIterator;
// ...
Iterator begin()
{
return Iterator(/*...*/);
}
ConstIterator const_begin() const
{
return ConstIterator(/*...*/);
}
// ...
};

int main() {
List<int> list;
List<int>::ConstIterator ci = list.const_begin();
List<int>::Iterator i = list.begin();
ci = i; // works fine as expected
i = ci; // fails as expected
i = i;  // works fine as expected
ci = ci;  // works fine as expected
return 0;
}
3

Другие решения

Других решений пока нет …