Неизменяемый контейнерный класс C ++ - PullRequest
9 голосов
/ 30 марта 2011

Скажите, что у меня есть класс C ++ Container, который содержит некоторые элементы типа Element. По разным причинам неэффективно, нежелательно, не нужно, нецелесообразно и / или невозможно (1) модифицировать или заменить содержимое после создания. Что-то вроде const std::list<const Element> (2).

Container может отвечать многим требованиям концепций STL «контейнер» и «последовательность». Он может предоставлять различные типы, такие как value_type, reference и т. Д. Он может предоставлять конструктор по умолчанию, конструктор копирования, тип const_iterator, begin() const, end() const, size, empty, все операторы сравнения, и, возможно, некоторые из rbegin() const, rend() const, front(), back(), operator[]() и at().

Однако Container не может предоставить insert, erase, clear, push_front, push_back, неконстантный front, неконстантный back, неконстантный operator[] или неконстантный at с ожидаемой семантикой. Таким образом, кажется, что Container не может квалифицироваться как «последовательность». Кроме того, Container не может предоставить operator= и swap, и он не может предоставить тип iterator, который указывает на неконстантный элемент. Таким образом, он даже не может считаться «контейнером».

Есть ли какая-нибудь менее способная концепция STL, с которой встречается Container? Есть ли «контейнер только для чтения» или «неизменный контейнер»?

Если Container не соответствует определенному уровню соответствия, есть ли значение в частичном соответствии? Это вводит в заблуждение, чтобы сделать его похожим на «контейнер», когда он не подходит? Есть ли краткий, однозначный способ документировать соответствие, чтобы мне не приходилось явно документировать соответствующую семантику? И точно так же, способ документировать это так, чтобы будущие пользователи знали, что они могут использовать общий код только для чтения, но не ожидают, что мутирующие алгоритмы будут работать?

Что я получу, если я ослаблю проблему, чтобы Container был Назначаемым (но его элементы - нет)? В этот момент возможны operator= и swap, но разыменование iterator все еще возвращает const Element. Container теперь квалифицируется как "контейнер"?

const std::list<T> имеет примерно тот же интерфейс, что и Container. Означает ли это, что это не «контейнер» и не «последовательность»?

Сноска (1) У меня есть варианты использования, которые охватывают весь этот спектр. У меня есть класс "потенциальный контейнер", который адаптирует некоторые данные только для чтения, поэтому он должен быть неизменным. У меня есть потенциальный контейнер, который генерирует собственное содержимое по мере необходимости, поэтому он изменчив, но вы не можете заменить элементы так, как этого требует STL. У меня еще есть еще один потенциальный контейнер, который хранит свои элементы таким образом, что insert() будет настолько медленным, что это никогда не будет полезным. И, наконец, у меня есть строка, которая хранит текст в UTF-8, предоставляя интерфейс, ориентированный на кодовую точку; изменчивая реализация возможна, но совершенно не нужна.

Сноска (2) Это только для иллюстрации. Я почти уверен, что std::list требует присваиваемого типа элемента.

Ответы [ 2 ]

2 голосов
/ 30 марта 2011

STL не определяет менее значимых понятий; главным образом потому, что идея const обычно выражается на уровне итератора или ссылки, а не на уровне класса.

Вы не должны предоставлять iterator неожиданную семантику, только предоставить const_iterator. Это позволяет сбою клиентского кода в наиболее логичном месте (с наиболее читаемым сообщением об ошибке), если они допускают ошибку.

Возможно, самый простой способ сделать это - заключить его в капсулу и предотвратить все неконстантные псевдонимы.

class example {
    std::list<sometype> stuff;
public:
    void Process(...) { ... }
    const std::list<sometype>& Results() { return stuff; }
};

Теперь любой клиентский код точно знает, что он может делать с возвращаемым значением Results-nada, которое требует мутации.

1 голос
/ 30 марта 2011

Пока ваш объект может предоставить соответствующего const_iterator, у него больше не должно быть ничего.Это должно быть довольно легко реализовать в вашем классе контейнера.

(Если применимо, посмотрите на библиотеку Boost.Iterators; в ней есть классы iterator_facade и iterator_adaptor, которые помогут вам с мельчайшими подробностями)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...