Как работает перегрузка оператора ++ для перебора моего LinkedList

Это мое пространство имен LinkedList

namespace LinkedList {     
    template<class T>
    class Node{
    public:
        Node(const T& data)
            :data_(data), next_(nullptr) {}
        T data_;
        Node<T> *next_;

        Node<T> *operator++(){
            return next_;
        }
    };


    

    template<class T>
    class LinkedList{
    public:
        LinkedList()
            : head_(nullptr), tail_(nullptr) {}
        
        ~LinkedList(){
            Node<T> *curr = head_;
            Node<T> *next;
            while(curr != nullptr){
                next = curr->next_;
                delete curr;
                curr = next;
            }
        }
        void Append(const T& data) {
            Node<T> *tmp = new Node<T>(data);
            if (head_ == nullptr) {
                head_ = tmp;
                tail_ = tmp;
            } else if (head_ == tail_){
                head_->next_ = tmp;
                tail_ = tmp;
            } else {
                tail_->next_ = tmp;
                tail_ = tmp;
            }
        }

        void Present(){
            for(Node<T> *curr = head_; curr != nullptr; curr=curr->next_){
                std::cout << curr->data_ << std::endl;
            }
        }

        Node<T> *begin(){
            return head_;
        }

        Node<T> *end(){
            return nullptr;
        }
    private:
        Node<T> *head_;
        Node<T> *tail_;
    };
}

Я читал Iterators и хотел, чтобы мой объект LinkedList был совместим с диапазоном на основе циклов for. Это то, что я получил, прочитав о циклах for на основе диапазона

for(auto x: list){ }

эквивалентно

for(; begin != end; ++begin) { //*begin};

Я думал, что был дерзким и мог пропустить пару шагов перегрузки оператора (!=, *), чтобы мой LinkedList работал с диапазоном, основанным на циклах for, запрограммировав мой итератор в мой класс Node ни по какой другой причине, кроме того, что мне так хотелось. Я только второй день изучаю С++, поэтому это может быть глупый вопрос, но я все еще немного смущен тем, почему это не работает:

LinkedList::LinkedList<int> list;
list.Append(3);
list.Append(5);
for(auto x: list) { //blah blah blah}

Я знаю, что приращение моего префикса является основной проблемой, когда цикл for выполняет ++__begin, я надеюсь, что он делает ___begin=_begin->next, но это не так. Почему это? На мой взгляд, перегрузка оператора работает следующим образом: любые переменные-члены, на которые я ссылаюсь в функции перегрузки, ссылаются на экземпляр, над которым я работаю. И когда я возвращаю ptr, он устанавливает любой экземпляр, над которым я работаю, на этот ptr. Я знаю, что мое понимание этого неверно, потому что это не работает, поэтому, пожалуйста, кто-нибудь объясните мне, как это на самом деле работает, лол.

Есть 2 отдельных оператора приращения, пре-инкремент и пост-инкремент. ++begin является предварительным приращением (++ идет перед переменной), и для итератора он должен изменять себя и возвращать себя. Это делает ваш план встраивания его в класс Node более или менее невозможным в этом сценарии.

super 11.12.2020 00:20

@super понял, вы невероятно полезны, спасибо!

killometers90 11.12.2020 00:23

@super, чтобы было ясно, в функции перегрузки вы действительно ссылаетесь на экземпляр, над которым работаете? и поскольку вы не можете установить это = чему-то другому, это невозможно?

killometers90 11.12.2020 00:26

Да. Вам нужен отдельный объект, который просто указывает на экземпляр Node, и когда вы делаете ++ на нем, вы модифицируете его, чтобы он указывал на следующий экземпляр Node.

super 11.12.2020 00:29

Возвращаемое значение фактически здесь не используется. Но именно так работают все итераторы в стандартной библиотеке, и почти любой, кто использует ваш код, будет ожидать, что ваш итератор будет делать то же самое.

super 11.12.2020 00:30

Полезное чтение: Написание собственного контейнера STL. Возможно, вам все это не понадобится, но вам понадобится довольно много классов итераторов, реализованных для работы на основе диапазона for.

user4581301 11.12.2020 00:34
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы написали

template<class T>
Node<T>* Node<T>::operator++();

который будет вызываться как

Node<int> n;
Node<int> *p = ++n; // calls the pre-increment operator

Обратите внимание, что это сильно отличается от канонической формы

Node<T>& Node<T>::operator++();

который возвращает ссылку на увеличенный объект, который можно использовать в выражении.

Но есть еще одна проблема: перегрузка оператора работает

Когда оператор появляется в выражении и по крайней мере один из его операндов имеет тип класса или тип перечисления,...

Но Node<T>* — это не тип класса. Это указатель, а указатели уже имеют свои собственные встроенные операторы. Их нельзя перегружать. Node<T>*::operator++() нет.

Напишите итератор. Это не так уж плохо, и они действительно работают!


NB. Итераторы предназначены для обобщения указателей и имеют совместимый интерфейс. Вы можете использовать необработанные указатели, если ваш контейнер в основном представляет собой массив, потому что увеличение указателя уже является правильным способом перебора массива.

Только когда мы хотим предоставить доступ, подобный массиву или указателю, к несмежной структуре, нам приходится выполнять эту дополнительную работу.

Оооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо в этом больше смысла...

killometers90 11.12.2020 00:57

Другие вопросы по теме