Tag Archives: map

Оператор [] для multimap

Наверное, не только у меня бывали моменты, когда отсутствие в std::multimap возможности оперировать блоками значений, отвечающих одному ключу, хотя бы в духе совместимости с std::map раздражало и казалось странным. В C++11 добавили хэш-таблицы, включая std::unordered_multimap — аналог std::multimap (сбалансированного дерева двоичного поиска), но расширение интерфейса в сторону работы с диапазонами не последовало. К сожалению, пример boost.range и языка D (см. также ссылки здесь) пока не находят отклика в Стандартной библиотеке C++.

Несколько месяцев назад я написал расширение, добавляющее оператор [] и аналогичную функцию at() в произвольный класс, совместимый с std::multimap или std::unordered_multimap. Подмешивание функционала происходит с помощью наследования от базового контейнера, переданного параметром шаблона. Традиционно принято считать, что наследовать от стандартных контейнеров — плохо, так как они изначально для этого не предназначены (невиртуальные деструкторы). В данном случае этим стереотипом можно пренебречь, так как класс-наследник не добавляет никакого состояния к объекту базового класса и никак не влияет на его жизненный цикл. Добавляемая функциональность могла бы быть реализована внешними методами, если бы они были в C++ (есть в D).

Оператор [] возвращает прокси-объект «диапазон» MapRangeView, для которого определён ряд операций (например, присваивание, begin/end), позволяющие оперировать группой элементов multimap как контейнером с минимальным функционалом (например, обходить их с помощью for(:)). Впрочем, данный класс предоставляет также ряд высокоуровневых функций (синтаксический сахар). Но есть и свои мелкие неприятности:

map1["key1"] = map2["key2"]; // копирование прокси-объекта
// содержимое map1 и map2 не поменялось

Я решил не нагружать базовый оператор присваивания подменой содержимого контейнера. Вместо этого добавлен ещё один очень простой прокси-класс BasicRange, в который оборачивается наш диапазон, если требуется скопировать или переместить его элементы (для перемещения используется std::move_iterator).

map1["key1"] = map2["key2"].copying(); // копирование элементов
map1["key3"] = map2["key4"].moving();  // перемещение элементов
map2["key4"].erase(); // удаление того, что осталось после перемещения

У получившегося кода есть свои недостатки, но, что более важно, я пока не пользовался им всерьёз и не могу сказать, что уверен в отсутствии опасных ошибок. Исходники: определение классов диапазонов, контейнер-обёртка multimap, простенькие тесты.

Реклама