Неизменяемый контейнер с изменяемым контентом

История начинается с того, что я думал довольно просто:

Мне нужно создать класс, который будет использовать некоторые 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;
};

Простите меня, пока я должен наказать себя, чтобы искупить мои грехи.