пользовательский итератор для пользовательского контейнера

Я проектирую контейнер, который представляет собой сетку, представленную в виде 1D-массива (шаблонного). Я публикую здесь фрагмент кода, на самом деле это еще не все. Он используется в качестве скользящей сетки занятости в роботизированном приложении, где каждая ячейка представляет небольшую область мира.

Одна операция, которую я часто выполняю с этой сеткой, состоит в том, чтобы пройти через все ячейки, а также получить их мировые координаты:

for( unsigned r=0; r<mygrid.rows_; ++r ) {
for( unsigned c=0; c<mygrid.cols_; ++c ) {
cell = mygrid.getRC(r,c);
mygrid.rcToXY(r,c,&x,&y);
}
}

Я хотел бы иметь итератор, который хранит все это: ячейка, ее координаты rc и ее координаты xy.

for( Grid<CellType>::const_iterator it=mygrid.begin(); it!=mygrid.end(); ++it ) {
cell = *it;
printf("%d %d %f %f\n", it.r(), it.c(), it.x(), it.y());
}

Следуя многочисленным ответам и учебным пособиям в Интернете, я предложил следующую реализацию, которая работает. Однако мне это кажется немного неуклюжим, и ради академии я бы хотел, чтобы это выглядело лучше. Также была бы отличная совместимость с STL.

template <class G, typename C>
class base_iterator
{
private:
G* grid_;
C* cell_;
unsigned r_, c_;  // local
double  x_, y_;

// this should be private with access for friends (Grid) only
// but I can't make it work
public:
base_iterator(G* grid, unsigned r, unsigned c) : grid_(grid), r_(r), c_(c)
{
cell_ = ( r<grid->rows_ && c<grid->cols_ ) ? &grid_->getRC(r,c) : 0;
grid_->rcToXY(r,c,&x_,&y_);
}public:
base_iterator() : grid_(0) { }

// used to cast an iterator to a const_iterator
template <class G2, typename C2>
base_iterator(const base_iterator<G2,C2>& other)
{
grid_ = other.grid();
cell_ = & other.cell();
r_ = other.r();
c_ = other.c();
x_ = other.x();
y_ = other.y();
}

// this should be private with access for friends only
G* grid() const { return grid_; }

C& cell() { return *cell_; }
const C& cell() const { return *cell_; }
unsigned r() const { return r_; }
unsigned c() const { return c_; }
double  x() const { return x_; }
double  y() const { return y_; }

C* operator->() { return cell_; }
const C* operator->() const { return cell_; }

C& operator*() { return *cell_; }
const C& operator*() const { return *cell_; }

//prefix
base_iterator & operator++()
{
// my iteration logic here which needs access to grid
// in order to find the number of rows, etc.
return *this;
}

//postfix
base_iterator operator++(int)
{
base_iterator it(*this);   // make a copy for result
++(*this);              // Now use the prefix version to do the work
return it;          // return the copy (the old) value.
}

template <class G2, typename C2>
bool operator==(const base_iterator<G2,C2> & other) const
{
return cell_ == &other.cell();
}

template <class G2, typename C2>
bool operator!=(const base_iterator<G2,C2>& other) const
{ return cell_!=other.cell(); }
};

И тогда в моем классе сетки:

  typedef base_iterator<Grid<T>,T> iterator;
typedef base_iterator<Grid<T> const, T const> const_iterator;

iterator begin() { return iterator(this,0,0); }
iterator end() { return iterator(this,rows_,cols_); }

const_iterator begin() const { return const_iterator(this,0,0); }
const_iterator end() const { return const_iterator(this,rows_,cols_); }

Опять же, это работает, но я чувствую, что это немного неуклюже (см. Комментарии в коде итератора), и я хотел бы знать, как я могу улучшить его. Я видел многочисленные посты об использовании фасадного итератора boost итератора, но я не мог понять, как адаптировать его к моему случаю.

2

Решение

Ну, я нашел одно решение, которое меня вполне устраивает. Вот полный список для справки. Есть некоторые хитрые части, которые заняли у меня некоторое время, в частности, чтобы можно было вывести реализацию за пределы объявлений классов. Мне не удалось сделать класс base_iterator не вложенным классом Grid, в соответствии с тем, что я читал здесь и там, я думаю, что это невозможно.

#include <cstdio>
#include <cassert>
#include <stdexcept>
#include <algorithm>template <class T>
class Grid
{
public:
// these should be private, with public getters...
double resolution_;
unsigned rows_, cols_;
int map_r0_, map_c0_; // grid coordinates of origin of map

private:
T* cell_;

public:
Grid(double resolution, unsigned rows, unsigned cols);
~Grid() { delete[] cell_; }

T & getRC(unsigned r, unsigned c);
const T & getRC(unsigned r, unsigned c) const;
void rcToXY(unsigned r, unsigned c, double* x, double* y) const;

public:
template <class GridType, class CellType>
class base_iterator : std::iterator<std::forward_iterator_tag, CellType>
{
private:
friend class Grid;
GridType* grid_;
CellType* cell_;
unsigned r_, c_;  // local
double  x_, y_;

base_iterator(GridType* grid, unsigned r, unsigned c);

public:
base_iterator() : grid_(0) { }

template <class G2, class C2>
base_iterator(const base_iterator<G2,C2>& other);

CellType& cell() { return *cell_; }
const CellType& cell() const { return *cell_; }
unsigned r() const { return r_; }
unsigned c() const { return c_; }
double  x() const { return x_; }
double  y() const { return y_; }

CellType* operator->() { return cell_; }
const CellType* operator->() const { return cell_; }

CellType& operator*() { return *cell_; }
const CellType& operator*() const { return *cell_; }

//prefix
base_iterator & operator++();

//postfix
base_iterator operator++(int);

template <class G2, class C2>
bool operator==(const base_iterator<G2,C2> & other) const
{ return cell_ == other.cell_; }

template <class G2, class C2>
bool operator!=(const base_iterator<G2,C2>& other) const
{ return cell_ != other.cell_; }
};

typedef base_iterator<Grid<T>,T> iterator;
typedef base_iterator<Grid<T> const, T const> const_iterator;

iterator begin() { return iterator(this,0,0); }
iterator end() { return iterator(this,rows_,0); }

const_iterator begin() const { return const_iterator(this,0,0); }
const_iterator end() const { return const_iterator(this,rows_,0); }
};template <class T>
Grid<T>::Grid(double resolution, unsigned rows, unsigned cols)
: resolution_(resolution), rows_(rows), cols_(cols), map_r0_(0), map_c0_(0)
{
cell_ = new T[rows_*cols_];
}

template <class T>
T & Grid<T>::getRC(unsigned r, unsigned c)
{
if (r >= rows_ || c >= cols_)
throw std::runtime_error("Out of bounds");
return cell_[r * cols_ + c];
}

template <class T>
const T & Grid<T>::getRC(unsigned r, unsigned c) const
{
if (r >= rows_ || c >= cols_)
throw std::runtime_error("Out of bounds");
return cell_[r * cols_ + c];
}

template <class T>
void Grid<T>::rcToXY(unsigned r, unsigned c, double* x, double* y) const
{
*x = (map_c0_ + c + 0.5) * resolution_;
*y = (map_r0_ + r + 0.5) * resolution_;
}template <class T>
template <class GridType, class CellType>
Grid<T>::base_iterator<GridType,CellType>::base_iterator(GridType* grid, unsigned r, unsigned c)
: grid_(grid), r_(r), c_(c)
{
if( r<grid->rows_ && c<grid->cols_ ) {
cell_ = &grid_->getRC(r,c);
grid_->rcToXY(r,c,&x_,&y_);
}
else
cell_ = &grid_->getRC(grid->rows_-1,grid->cols_-1) + 1;
}

// beautiful triple template declaration !
template <class T>
template <class GridType, class CellType>
template <class G2, class C2>
Grid<T>::base_iterator<GridType,CellType>::base_iterator(const Grid<T>::base_iterator<G2,C2>& other)
{
grid_ = other.grid_;
cell_ = other.cell_;
r_ = other.r();
c_ = other.c();
x_ = other.x();
y_ = other.y();
}

template <class T>
template <class GridType, class CellType>
Grid<T>::base_iterator<GridType,CellType> & Grid<T>::base_iterator<GridType,CellType>::operator++()
{
assert( grid_!=0 );

if( c_==grid_->cols_-1 )
{
c_ = 0;
x_ = (grid_->map_c0_ + 0.5) * grid_->resolution_;
++r_;
y_ += grid_->resolution_;
}
else
{
++c_;
x_ += grid_->resolution_;
}
++cell_;

return *this;
}

template <class T>
template <class GridType, class CellType>
Grid<T>::base_iterator<GridType,CellType> Grid<T>::base_iterator<GridType,CellType>::operator++(int)
{
base_iterator it(*this);   // make a copy for result
++(*this);              // Now use the prefix version to do the work
return it;          // return the copy (the old) value.
}

void print(unsigned i)
{
printf("%d ", i);
}

int main()
{
Grid<unsigned> mygrid(.1,2,3);
unsigned ctr=0;
for( Grid<unsigned>::iterator it=mygrid.begin(); it!=mygrid.end(); ++it )
*it = ctr++;

ctr = 0;
printf("All elements: r, c, x, y, value\n");
for( Grid<unsigned>::const_iterator it=mygrid.begin(); it!=mygrid.end(); ++it ) {
assert( *it == ctr++ );
printf("%d %d %f %f %d\n", it.r(), it.c(), it.x(), it.y(), *it);
}

printf("All elements values: ");
std::for_each(mygrid.begin(), mygrid.end(), print);
printf("\n");

return 0;
}
1

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

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

По вопросам рекламы [email protected]