Почему мой итератор не работает с алгоритмом std::copy?

Почему мой класс Iterator внутри MojVektor (что-то вроде std::vector) не работает с алгоритмом std::copy()?

МойВектор.hpp

#pragma once
#include <iostream>
#include <initializer_list>
#include <algorithm>
#include <iterator>

template <typename T>
class MojVektor {
private:
    size_t capacity_;
    size_t size_;
    T* arr_;

    void realoc() {
        capacity_ *= 2;
        T* tempArr = arr_;
        arr_ = new T[capacity_];
        std::copy(tempArr, tempArr + size_, arr_);
        delete[] tempArr;
        return;
    }

public:
    class Iterator;

    MojVektor() : capacity_{ 10 }, size_{ 0 }, arr_{ new T[capacity_] } {};

    MojVektor(const std::initializer_list<T>& list) : capacity_{ list.size() }, size_{ list.size() }, arr_{ new T[capacity_] } {
        std::copy(list.begin(), list.end(), arr_);
        return;
    }

    MojVektor(const MojVektor& second) : capacity_{ second.capacity_ }, size_{ second.size_ }, arr_{ new T[capacity_] } {
        std::copy(second.arr_, second.arr_ + size_, arr_);
        return;
    }

    MojVektor& operator=(const MojVektor& second) {
        capacity_ = second.capacity_;
        size_ = second.size_;
        arr_ = new T[capacity_];
        std::copy(second.arr_, second.arr_ + size_, arr_);
        return *this;
    }

    MojVektor(MojVektor&& second) : capacity_{ std::move(second.capacity_) }, size_{ std::move(second.size_) }, arr_{ std::move(second.arr_) } {
        second.capacity_ = 0;
        second.size_ = 0;
        second.arr_ = nullptr;
        return;
    }

    MojVektor& operator=(MojVektor&& second) {
        capacity_ = std::move(second.capacity_);
        size_ = std::move(second.size_);
        arr_ = std::move(second.arr_);
        second.capacity_ = 0;
        second.size_ = 0;
        second.arr_ = nullptr;
        return *this;
    }

    ~MojVektor() {
        delete[] arr_;
        size_ = 0;
        capacity_ = 0;
        return;
    }

    MojVektor& push_back(const T& e) {
        if (arr_ == nullptr) {
            capacity_ = 10;
            arr_ = new T[capacity_];
        }
        if (size_ >= capacity_) {
            realoc();
        }
        arr_[size_] = e;
        ++size_;
        return *this;
    }

    MojVektor& push_front(const T& e) {
        if (arr_ == nullptr) {
            capacity_ = 10;
            arr_ = new T[capacity_];
        }
        if (size_ >= capacity_) {
            realoc();
        }
        for (T* endIndex = arr_ + size_ - 1; endIndex >= arr_; --endIndex) {
            *(endIndex + 1) = std::move(*(endIndex));
        }
        *(arr_) = e;
        ++size_;
        return *this;
    }

    MojVektor& push_back(T&& e) {
        if (arr_ == nullptr) {
            capacity_ = 10;
            arr_ = new T[capacity_];
        }
        if (size_ >= capacity_) {
            realoc();
        }
        arr_[size_] = std::move(e);
        ++size_;
        return *this;
    }

    MojVektor& push_front(T&& e) {
        if (arr_ == nullptr) {
            capacity_ = 10;
            arr_ = new T[capacity_];
        }
        if (size_ >= capacity_) {
            realoc();
        }
        for (T* endIndex = arr_ + size_ - 1; endIndex >= arr_; --endIndex) {
            *(endIndex + 1) = std::move(*(endIndex));
        }
        *(arr_) = std::move(e);
        ++size_;
        return *this;
    }

    size_t size() const {
        return size_;
    }

    T& at(size_t index) const {
        if (index < 0 || index > size_ - 1) {
            throw std::out_of_range("Index out of range!");
        }
        return *(arr_ + index);
    }

    T& operator[](size_t index) const {
        return *(arr_ + index);
    }

    void clear() {
        capacity_ = 0;
        size_ = 0;
        delete[] arr_;
        arr_ = nullptr;
        return;
    }

    void resize(size_t newSize, const T& difference_value) {
        if (newSize < size_) {
            size_ = newSize;
        }
        else if (newSize > size_) {
            T* tempArr = arr_;
            capacity_ = newSize;
            arr_ = new T[capacity_];
            std::copy(tempArr, tempArr + size_, arr_);
            while (size_ < newSize) {
                arr_[size_] = difference_value;
                ++size_;
            }
        }
        return;
    }

    MojVektor& pop_back() {
        if (size_ == 0) {
            throw std::out_of_range("Vector is empty!");
        }
        --size_;
        return *this;
    }

    MojVektor& pop_front() {
        if (size_ == 0) {
            throw std::out_of_range("Vector is empty!");
        }
        for (T* beginIndex = arr_; beginIndex < arr_ + size_; ++beginIndex) {
            *(beginIndex) = std::move(*(beginIndex + 1));
        }
        --size_;
        return *this;
    }

    T& back() const {
        if (size_ == 0) {
            throw std::out_of_range("Vector is empty!");
        }
        return arr_[size_ - 1];
    }

    T& front() const {
        if (size_ == 0) {
            throw std::out_of_range("Vector is empty!");
        }
        return *arr_;
    }

    bool empty() const {
        return size_ == 0;
    }

    size_t capacity() const {
        return capacity_;
    }

    bool operator==(const MojVektor& second) const {
        if (size_ != second.size_) {
            return false;
        }
        for (size_t i = 0; i < size_; ++i) {
            if ((this->operator[](i)) != second[i]) {
                return false;
            }
        }
        return true;
    }

    bool operator!=(const MojVektor& second) const {
        return !(this->operator==(second));
    }

    bool full() const {
        return size_ == capacity_;
    }

    MojVektor subvector(Iterator beginIt, Iterator endIt) const {
        if (beginIt < begin() || endIt > end()) {
            throw std::out_of_range("Iterators out of range!");
        }
        MojVektor v;
        while (beginIt != endIt) {
            v.push_back(*beginIt);
            ++beginIt;
        }
        return v;
    }

    Iterator begin() const {
        return Iterator(arr_);
    }

    Iterator end() const {
        return Iterator(arr_ + size_);
    }

    Iterator find(const T& value) const {
        Iterator it = begin();
        while (it != end()) {
            if (*(it) == value) {
                return it;
            }
            ++it;
        }
        return it;
    }

    Iterator erase(Iterator pos) {
        if (pos < begin() || pos > end()) {
            throw std::out_of_range("Iterator out of range!");
        }
        if (pos == end()) {
            return end();
        }
        Iterator it{ pos };
        while (it != end()) {
            *(it) = std::move(*(it + 1));
            ++it;
        }
        --size_;
        return pos;
    }

    Iterator insert(Iterator pos, const T& e) {
        size_t index = pos - begin();
        if (index < 0 || index >= size_) {
            throw std::out_of_range("Iterator out of range!");
        }
        if (size_ >= capacity_) {
            realoc();
        }
        if (index == 0) {
            push_front(e);
        }
        else {
            for (size_t i = size_; i >= index; --i) {
                *(arr_ + i + 1) = std::move(*(arr_ + i));
            }
            arr_[index] = e;
            ++size_;
        }
        return Iterator{ arr_ + index };
    }

    Iterator insert(Iterator pos, T&& e) {
        size_t index = pos - begin();
        if (index < 0 || index >= size_) {
            throw std::out_of_range("Iterator out of range!");
        }
        if (size_ >= capacity_) {
            realoc();
        }
        if (index == 0) {
            push_front(std::move(e));
        }
        else {
            for (size_t i = size_; i >= index; --i) {
                *(arr_ + i + 1) = std::move(*(arr_ + i));
            }
            arr_[index] = std::move(e);
            ++size_;
        }
        return Iterator{ arr_ + index };
    }

    Iterator rbegin() const {
        return end() - 1;
    }

    Iterator rend() const {
        return begin() - 1;
    }

    Iterator erase(Iterator beginIt, Iterator endIt) {
        size_t beginIndex = beginIt - begin();
        size_t endIndex = endIt - begin();
        if (beginIndex > endIndex || beginIndex < 0 || endIndex > size_ || beginIndex > size_) {
            throw std::out_of_range("Iterators out of range!");
        }
        for (size_t i = beginIndex; i < endIndex; ++i) {
            arr_[i] = std::move(arr_[endIndex - beginIndex + i]);
        }
        size_ -= (endIndex - beginIndex);
        return Iterator{ arr_ + beginIndex };
    }

    void rotate() {
        for (size_t beginIndex = 0, endIndex = size_ - 1; beginIndex < endIndex; ++beginIndex, --endIndex) {
            std::swap(arr_[beginIndex], arr_[endIndex]);
        }
        return;
    }

    void rotate(Iterator beginIt, Iterator endIt) {
        size_t beginIndex = beginIt - Iterator(arr_);
        size_t endIndex = (endIt - Iterator(arr_)) - 1;
        if (beginIndex > endIndex || beginIndex < 0 || endIndex > size_ || beginIndex > size_) {
            throw std::out_of_range("Iterators out of range!");
        }
        while (beginIndex < endIndex) {
            std::swap(arr_[beginIndex], arr_[endIndex]);
            ++beginIndex;
            --endIndex;
        }
        return;
    }

    T* data() {
        return arr_;
    }
};

template <typename T>
std::ostream& operator<<(std::ostream& outputStream, const MojVektor<T>& container) {
    const size_t size = container.size();
    outputStream << "{";
    for (size_t i = 0; i < size; ++i) {
        outputStream << container[i];
        if (i + 1 < size) {
            outputStream << ", ";
        }
    }
    outputStream << "}";
    return outputStream;
}

template <typename T>
class MojVektor<T>::Iterator : public std::iterator<std::random_access_iterator_tag, T> {
private:
    T* ptr_;

public:

    Iterator() : ptr_{ nullptr } {};

    Iterator(T* ptr) : ptr_{ ptr } {};

    Iterator(T& e) : ptr_{ &e } {};

    Iterator(const Iterator& second) : ptr_{ second.ptr_ } {};

    Iterator(Iterator&& second) : ptr_(std::move(second.ptr_)) {
        second.ptr_ = nullptr;
        return;
    }

    Iterator& operator=(const Iterator& second) {
        ptr_ = second.ptr_;
        return *this;
    }

    Iterator& operator=(Iterator&& second) {
        ptr_ = std::move(second.ptr_);
        second.ptr_ = nullptr;
        return *this;
    }

    T& operator*() {
        return *ptr_;
    }

    Iterator& operator++() {
        ++ptr_;
        return *this;
    }

    Iterator operator++(int) {
        Iterator returnValue{ ptr_ };
        ++ptr_;
        return returnValue;
    }

    Iterator& operator--() {
        --ptr_;
        return *this;
    }

    Iterator operator--(int) {
        Iterator returnValue{ ptr_ };
        --ptr_;
        return returnValue;
    }

    Iterator& operator+=(size_t n) {
        ptr_ += n;
        return *this;
    }

    Iterator& operator-=(size_t n) {
        ptr_ -= n;
        return *this;
    }

    Iterator operator+(size_t n) const {
        return Iterator(ptr_ + n);
    }

    Iterator operator-(size_t n) const {
        return Iterator(ptr_ - n);
    }

    T* operator->() {
        return ptr_;
    }

    size_t operator-(const Iterator& second) const {
        return ptr_ - second.ptr_;
    }

    T& operator[](size_t index) const {
        return *(ptr_ + index);
    }

    bool operator==(const Iterator& second) const {
        return ptr_ == second.ptr_;
    }

    bool operator!=(const Iterator& second) const {
        return !(this->operator==(second));
    }

    bool operator<(const Iterator& second) const {
        return ptr_ < second.ptr_;
    }

    bool operator>(const Iterator& second) const {
        return ptr_ > second.ptr_;
    }

    bool operator<=(const Iterator& second) const {
        return ptr_ <= second.ptr_;
    }

    bool operator>=(const Iterator& second) const {
        return ptr_ >= second.ptr_;
    }
};

main.cpp

#include <iostream>
#include "MojVektor.hpp"

#include <algorithm>

int main() 
{
    MojVektor<int> a{1, 2, 3};
    MojVektor<int> b;
    std::copy(a.begin(), a.end(), b.begin());
    std::cout << a << std::endl;
    std::cout << b << std::endl;

    return 0;
}

Я попробовал вручную установить std::iterator_tag, value_type и т. д. с using, using и наследованием, typedef и typedef и наследованием. Я также рассмотрел некоторые вопросы, заданные ранее, но они были связаны со списками и вставками.

Также в ответы добавили наследование, которое уже присутствует в моем коде.

Хорошо, я посмотрел, и вот что происходит, поэтому, когда я делаю std::copy, он фактически копирует элементы вектора, но size_ остается прежним, что означает, что итератор работает. Теперь, как мне изменить size_ с помощью std::copy? @273K

Elnur1337 11.04.2024 23:24

std::copy() не меняет целевой размер. Вы несете ответственность за изменение размера цели до необходимого наименьшего размера перед вызовом std::copy().

3CxEZiVlQ 11.04.2024 23:27

Спасибо за ответ, а также за отладчик, я совсем забыл об отладчике.

Elnur1337 11.04.2024 23:29

@ Elnur1337 MojVektor& operator= -- утечка памяти. Вам не удалось delete[] восстановить старую память. Лучше просто написать MojVektor& operator=(const MojVecktor& rhs) { MojVecktor temp(rhs); std::swap(temp.arr_, arr_); std::swap(temp.size_, size_); std::swap(temp.capacity_, capacity_); return *this; }

PaulMcKenzie 11.04.2024 23:45

@PaulMcKenzie Пожалуйста, не рекомендуйте писать неэффективные копии. Копий бывает много, и они заслуживают того, чтобы их писали с осторожностью. Смотрите это.

Passer By 12.04.2024 06:03

@PaulMcKenzie нет, это не так, именно так это и должно работать: Google Copyoperator= и Moveoperator=.

Elnur1337 12.04.2024 08:17

@ Elnur1337 — Утечка памяти, и точка. google copyoperator= -- Нет, мне не нужно это гуглить. Во-первых, никакого перемещения не происходит, так почему же вы упомянули оператор перемещения? Во-вторых, эта простая программа показывает утечку: int main() { MojVektor<int> a, b; b = a; }. Вы дважды вызываете new для участника arr_ без обращения к delete ранее выделенной памяти. Если только вы не придумали что-то, чего не осознает ни один программист C++, это утечка памяти.

PaulMcKenzie 12.04.2024 11:44

Посмотрите правило 3. Где ваш оператор присваивания что-либо делает с ранее выделенной памятью? Я не уверен, какие еще языки программирования вы использовали, но в C++ нет «сборщика мусора», где вы можете просто непрерывно вызывать new[] для переменной, и волшебным образом ранее выделенная new[] просто «уходит». C++ не работает таким образом. Каждый вызов new, который вы делаете, должен сопровождаться соответствующим delete, один-к-одному.

PaulMcKenzie 12.04.2024 11:50

@PaulMcKenzie предполагается копировать элементы, вот что означает копирование, и поэтому он называется оператором копирования =, а не оператором перемещения =. Посмотрите на ссылки rvalue и lvalue. Никакого перемещения не делается, потому что его там делать не следует, потому что Я ХОЧУ копировать из одного массива в другой в куче. Я создаю новый массив с теми же элементами, указатель не теряется ни для одного из двух массивов. И я знаю, что в C++ нет сборщика мусора. Это всего лишь два отдельных массива, содержащих одинаковые элементы.

Elnur1337 12.04.2024 15:38

Если вы настаиваете на своем неправильном подходе, продолжайте. В коде происходит утечка памяти. Я связал вас с правилом 3 — вы ясно видите пример delete копирования старых данных перед копированием переданных данных. Задача оператора присваивания состоит в том, чтобы избавиться от того, что было у объекта, а затем скопировать внутренности того, что было передано. Если вы этого не сделаете (очистите текущее состояние объекта), у вас возникнет утечка памяти. И просто чтобы вы знали: ваш оператор присваивания перемещения также приводит к утечке памяти, и все потому, что вы не смогли очистить объект перед перемещением.

PaulMcKenzie 12.04.2024 15:51

@PaulMcKenzie, я понял, что вы имели в виду, спасибо, но в следующий раз, пожалуйста, будьте более конкретны, я думал, вы говорите о чем-то другом в операторе =. Спасибо.

Elnur1337 12.04.2024 15:53

Я рад, что вы понимаете, в чем проблема. Однако я не могу сказать ничего более конкретного, чем сказать «утечка памяти». Утечка памяти вызвана не освобождением выделенной памяти.

PaulMcKenzie 12.04.2024 16:30
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
13
92
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Основная проблема заключается в том, что std::copy ожидает, что действительный диапазон назначения будет увеличиваться и присваивать ему значения. В вашем коде диапазон b пуст, поэтому std::copy не может его перебирать.

Следовательно, согласно вашей MojVektor реализации, вам нужно

  1. либо MojVektor::resize до std::copy: https://gcc.godbolt.org/z/sMTeEfbKc

    MojVektor<int> b;
    b.resize(a.size(), {});  // resize!
    std::copy(a.begin(), a.end(), b.begin());
    
  2. или используйте std::back_inserter : https://gcc.godbolt.org/z/3Mv5sf7aE

    MojVektor<int> b;
    std::copy(a.begin(), a.end(), std::back_inserter(b));
    

Другие проблемы:

  • Класс std::iterator устарел в C++17, поэтому не рекомендуется создавать зависимости/наследовать от него.
  • Вам не хватает необходимых псевдонимов типов (так называемых «типов членов») для вашего MojVektor класса.
  • Вам не хватает необходимых псевдонимов типов для вашего Iterator класса.

Это означает, что вам нужно реализовать свой контейнер и выполнять итерации в стиле std, чтобы вы могли правильно использовать std-алгоритмы.

template <typename T> class MojVektor 
{
public:
    // type aliases
    using value_type = T;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;
    using reference = value_type&;
    using const_reference = const value_type&;
    using pointer = T*;    
    // .... so on
};

// .....

template <typename T> class Iterator 
{
public:
    // type aliases
    using iterator_category = std::random_access_iterator_tag;
    using value_type = T;
    using difference_type = std::ptrdiff_t;
    using pointer = T*;
    using reference = T&;    
    // .... so on
};

У вас неопределенное поведение в вашей программе. Когда вы объявляете MojVektor<int> b;, вы объявляете пустой вектор.

Когда вы вызываете copy(a.begin(), a.end(), b.begin()), вы записываете (и увеличиваете) b.begin(), что аналогично b.end() и не подлежит разыменованию и не увеличивается.

В вашем тесте вам понадобится back_inserter, который выполняет push_back для добавления элементов в целевой контейнер.

А вот как это будет выглядеть при использовании back_inserter и разного. остальные исправления: godbolt.org/z/476rozEY9

Ted Lyngmo 11.04.2024 23:40

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