Разделение задач: контейнер отвечает за свои инварианты, итераторы за обход. Если вы перенесете ремонт в контейнер, вы можете отделить логические const
от mutable
, скрытых частей.
Можете ли вы написать своим итераторам «глупейший» способ отделить их от контейнера? Например, хранение числового индекса (если это имеет смысл для вашего контейнера), а затем вызов частного друга (или нескольких) контейнера для доступа к логическому n-му элементу.
Личные друзья могут быть перегружены на const
и могут по-прежнему изменять mutable
детали для выполнения ремонта, который вы описываете, а затем возвращать элемент.
(сокращенный) пример контейнера, поддерживающего произвольный доступ (и, следовательно, числовые индексы для доступа):
template<typename T>
class vector {
mutable std::vector<std::weak_ptr<T>> data; // notice mutable
T&
fetch(int n);
T const&
fetch(int n) const; // notice const overload
public:
class const_iterator;
friend class const_iterator;
const_iterator
begin() const;
};
template<typename T>
class vector<T>::const_iterator {
int index;
vector<T> const* this_; // notice const
public:
// constructors go here
const_iterator&
operator++()
{ ++index; }
// ...
T const&
operator*() const
{ return this_->fetch(index); } // this will call the const version of fetch
};
// example implementation of the const version of fetch
template<typename T>
T const&
vector<T>::fetch(int n) const
{
auto removed = std::remove_if(data.begin(), data.end(), [](std::weak_ptr<T>& element)
{ return element.expired(); });
// mutate mutable data member in a logically const member
data.erase(data.begin(), removed);
// this assumes that there is no race condition
// bear with me for the sake of understanding the mutable keyword
return *data[n].lock();
}