Неизменяемый контейнер с изменяемым контентом
История начинается с того, что я думал довольно просто:
Мне нужно создать класс, который будет использовать некоторые STL-контейнеры. Мне нужно предоставить пользователям доступ класса к неизменяемой версии этих контейнеров. Я не хочу, чтобы пользователи могли изменять контейнер (они не могут push_back()
в списке, например), но я хочу, чтобы пользователи могли изменять содержащиеся объекты (получить элемент с back()
и изменить его)
class Foo
{
public:
// [...]
ImmutableListWithMutableElementsType getImmutableListWithMutableElements();
// [...]
};
// [...]
myList = foo.getImmutableListWithMutableElements();
myElement = myList.back();
myElement.change(42); // OK
// [...]
// myList.push_back(myOtherElement); // Not possible
На первый взгляд кажется, что контейнер const будет работать. Но, конечно, вы можете использовать только константный итератор в контейнере const, и вы не можете изменить его.
На первый взгляд, такие вещи, как специализированный контейнер или итератор, приходят на ум. Я, вероятно, в конечном итоге с этим.
Тогда моя мысль: "Кто-то, должно быть, уже это сделал!" или "Элегантное, общее решение должно существовать!" и я здесь задаю свой первый вопрос о SO:
Как вы проектируете/трансформируете стандартный контейнер в неизменяемый контейнер с изменяемым контентом?
Я работаю над этим, но чувствую, что кто-то просто скажет: "Эй, я делаю это каждый раз, это легко, смотри!", поэтому я спрашиваю...
Спасибо за любые подсказки, предложения или замечательные общие способы сделать это:)
EDIT:
После некоторых экспериментов я столкнулся со стандартными контейнерами, которые обрабатывают некоторые специально украшенные интеллектуальные указатели. Это близко к ответу Николая.
Идея непреложного контейнера изменчивых элементов не является концепцией убийства, см. интересные заметки в ответе Оли.
Идея конкретного итератора, конечно, конечно, но это кажется непрактичным, поскольку мне нужно адаптироваться к любому контейнеру.
Спасибо вам всем за вашу помощь.
Ответы
Ответ 1
Самый простой вариант, вероятно, будет стандартным контейнером STL указателей, поскольку const
-ness не распространяется на фактические объекты. Одна из проблем заключается в том, что STL не очищает кучу памяти, которую вы выделили. Для этого взгляните на Boost Pointer Container Library или умные указатели.
Ответ 2
Вместо того, чтобы предоставлять пользователю весь контейнер, вы могли бы просто предоставить им неконстантные итераторы для начала и конца? Это путь STL.
Ответ 3
Вам нужен персонализированный итератор структуры данных, обертка вокруг вашего частного списка.
template<typename T>
class inmutable_list_it {
public:
inmutable_list_it(std::list<T>* real_list) : real_list_(real_list) {}
T first() { return *(real_list_->begin()); }
// Reset Iteration
void reset() { it_ = real_list_->begin(); }
// Returns current item
T current() { return *it_; }
// Returns true if the iterator has a next element.
bool hasNext();
private:
std::list<T>* real_list_;
std::list<T>::iterator it_;
};
Ответ 4
Болезненное решение:
/* YOU HAVE NOT SEEN THIS */
struct mutable_int {
mutable_int(int v = 0) : v(v) { }
operator int(void) const { return v; }
mutable_int const &operator=(int nv) const { v = nv; return *this; }
mutable int v;
};
Простите меня, пока я должен наказать себя, чтобы искупить мои грехи.