Я изучаю курс ООП (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-итераторы и завершите контейнер списка без них. Мне нужно время, чтобы понять ваш код» (как вы видите, мой код довольно прост, на мой взгляд). Как я понял, он не знал ответа на этот вопрос. Но я очень хочу знать, как это сделать! Как я могу решить эту проблему?
Извините за массу ошибок — я не являюсь носителем английского языка.
Вы действительно можете использовать <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;
}
Других решений пока нет …