Не удается заставить BOOST_FOREACH работать с моим пользовательским классом

Я реализовал тривиальный класс MyClass который имеет массив с new внутри (я знаю, что могу использовать контейнер STL, но я пытаюсь понять, как они работают). Я также создал подкласс итератора, способный перебирать все элементы MyClass объект:

class MyIterator : public iterator<forward_iterator_tag,int>
{
private:
int* data= nullptr;
int length;
int pointer=0;
int nullvalue=0;
public:
MyIterator(int* data, int length, bool end= false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
~MyIterator()
{
delete[] data;
}
MyIterator& operator++()
{
if(pointer!= length-1)
{
pointer++;
}
else
{
pointer= -1;
}
return *this;
}
bool operator==(const MyIterator& other)
{
return pointer==other.pointer;
}
bool operator!=(const MyIterator& other)
{
return pointer!= other.pointer;
}
int& operator* ()
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
};

class MyClass
{
private:
int* data= nullptr;
int length= 100;
public:
MyClass()
{
data= new int[length];
for(int i=0; i<length;i++)
data[i]=i+1;
}
iterator<forward_iterator_tag,int> begin()
{
return MyIterator(data,length);
}
iterator<forward_iterator_tag,int> end()
{
return MyIterator(data,length,true);
}
};

Пока итератор работает, если я использую его таким образом:

for(MyIterator i= MyClass_instance.begin(); i!=MyClass_instance.end();i++) {...}

Это не сработает, если я попытаюсь использовать его с BOOST_FOREACH:

BOOST_FOREACH(int i, MyClass_instance) {...}

Это ошибки, которые я получаю:

введите описание изображения здесь

1

Решение

  • Вы разрезаете свой итератор, возвращая их как std::iterator<> по значению. Тебе этого не сделать.

    Возврат по ссылке поможет избежать проблем с нарезкой но вводит худшую проблему: он возвращает ссылку на временный объект.

    Следовательно, исправьте это, вернув свой фактический Тип итератора по значению.

  • В вашем типе отсутствует константный итератор.

  • Все члены итератора не были правильными.

  • Также согласно странице растяжимость похоже, вам нужно добавить

    namespace boost {
    template<> struct range_mutable_iterator<MyClass> {
    typedef MyClass::MyIterator type;
    };
    
    template<> struct range_const_iterator<MyClass> {
    typedef MyClass::MyConstIterator type;
    };
    }
    
  • Есть серьезный проблема с реализацией правила три для вашего типа итератора (Что такое правило трех?).

    Вы удаляете данные контейнера каждый раз, когда итератор исчезает. А также MyClass сам никогда не освобождает данные …

Исправление большинства (?) Из вышеперечисленного:

Жить на Колиру

#include <iterator>
#include <boost/foreach.hpp>

class MyClass
{
private:
int* data  = nullptr;
int length = 100;

public:
class MyIterator : public std::iterator<std::forward_iterator_tag, int>
{
public:
MyIterator(int* data, int length, bool end = false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
MyIterator& operator++()
{
if(pointer!= length-1) {
pointer++;
}
else {
pointer= -1;
}
return *this;
}

bool operator==(const MyIterator& other) const { return pointer==other.pointer; }
bool operator!=(const MyIterator& other) const { return pointer!= other.pointer; }
int& operator*() const
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
private:
value_type* data     = nullptr;
int length;
int pointer          = 0;
mutable value_type nullvalue = 0;
};

class MyConstIterator : public std::iterator<std::forward_iterator_tag, const int>
{
public:
MyConstIterator(int const* data, int length, bool end = false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
MyConstIterator& operator++()
{
if(pointer!= length-1) {
pointer++;
}
else {
pointer= -1;
}
return *this;
}

bool operator==(const MyConstIterator& other) const { return pointer==other.pointer; }
bool operator!=(const MyConstIterator& other) const { return pointer!= other.pointer; }
int const& operator*() const
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
private:
value_type* data     = nullptr;
int length;
int pointer          = 0;
value_type nullvalue = 0;
};

public:
typedef MyIterator iterator_type;
typedef MyConstIterator const_iterator_type;

MyClass()
{
data= new int[length];
for(int i=0; i<length;i++)
data[i]=i+1;
}
~MyClass() {
delete[] data;
}
iterator_type begin()             { return MyIterator(data,length);      }
iterator_type end()               { return MyIterator(data,length,true); }
const_iterator_type begin() const { return MyConstIterator(data,length);      }
const_iterator_type end()   const { return MyConstIterator(data,length,true); }
};

namespace boost {
template<> struct range_mutable_iterator<MyClass> {
typedef MyClass::MyIterator type;
};

template<> struct range_const_iterator<MyClass> {
typedef MyClass::MyConstIterator type;
};
}

#include <iostream>

int main()
{
MyClass c;
BOOST_FOREACH(int i, c) {
std::cout << i << "\n";
}
}

¹ (если вы не храните итераторы где-то еще, но это было бы огромным анти-паттерном по многим причинам)

3

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