Выбрать главу

Недостаточно? Данные контейнера не передаются через интерфейс С, поскольку данная возможность поддерживается только для vector (совет 16). Вы не сможете создать экземпляр контейнера с типом bool — как будет показано в совете 18, vector<bool> не всегда ведет себя как vector и никогда не хранит настоящие логические величины. Вы даже не можете рассчитывать на постоянное время вставки-удаления, характерное для list, поскольку в vector и deque эти операции выполняются с линейной сложностью.

Что же остается после всего сказанного? «Обобщенный последовательный контейнер», в котором нельзя использовать reserve, capacity, operator[], push_front, pop_front, splice и вообще любой алгоритм, работающий с итераторами произвольного доступа; контейнер, у которого любой вызов insert и erase выполняется с линейной сложностью и приводит к недействительности всех итераторов, указателей и ссылок; контейнер, несовместимый с языком С и не позволяющий хранить логические величины. Захочется ли вам использовать подобный контейнер в своем приложении? Вряд ли.

Если умерить амбиции и отказаться от поддержки list, вы все равно теряете reserve, capacity, push_front и pop_front; вам также придется полагать, что вызовы insert и erase выполняются с линейной сложностью, а все итераторы, указатели и ссылки становятся недействительными; вы все равно теряете совместимость с С и не можете хранить в контейнере логические величины.

Даже если отказаться от последовательных контейнеров и взяться за ассоциативные контейнеры, дело обстоит не лучше. Написать код, который бы одновременно работал с set и map, практически невозможно, поскольку в set хранятся одиночные объекты, а в map хранятся пары объектов. Даже совместимость с set и multiset (или map и multimap) обеспечивается с большим трудом. Функция insert, которой при вызове передается только значение вставляемого элемента, возвращает разные типы для set/map и их multi-аналогов, при этом вы должны избегать любых допущений относительно того, сколько экземпляров данной величины хранится в контейнере. При работе с map и multimap приходится обходиться без оператора [ ], поскольку эта функция существует только в map.

Согласитесь, игра не стоит свеч. Контейнеры действительно отличаются друг от друга, обладают разными достоинствами и недостатками. Они не были рассчитаны на взаимозаменяемость, и с этим фактом остается только смириться. Любые попытки лишь искушают судьбу, а она этого не любит.

Но рано или поздно наступит день, когда окажется, что первоначальный выбор контейнера был, мягко говоря, не оптимальным, и вы захотите переключиться на другой тип. При изменении типа контейнера нужно не только исправить ошибки, обнаруженные компилятором, но и проанализировать весь код, где он используется, и разобраться, что следует изменить в свете характеристик нового контейнера и правил перехода итераторов, указателей и ссылок в недействительное состояние. Переходя с vector на другой тип контейнера, вы уже не сможете рассчитывать на С-совместимую структуру памяти, а при обратном переходе нужно проследить за тем, чтобы контейнер не использовался для хранения bool.

Если вы знаете, что тип контейнера в будущем может измениться, эти изменения можно упростить обычным способом — инкапсуляцией. Одно из простейших решений основано на использовании определений typedef для типов контейнера и итератора. Следовательно, фрагмент

class Widget{...};

vector<Widget> vw;

Widget bestWidget;

… // Присвоить значение bestWidget

vector<Widget>::iterator i =// Найти Widget с таким же значением,

find(vw.begin(),vw.end().bestWidget) // как у bestWidget