return keyLess(lhs.first, rhs.first); // Определение keyLess
} // приведено ниже
bool operator(const Data& lhs, // Функция сравнения
const Data::first_type& k) const // для поиска (форма 1)
{
return keyLess(lhs.first, rhs.first);
}
bool operator(const Data::first_type& k, // Функция сравнения
const Data& rhs) const; // для поиска (форма 2)
{
return keyLess(k.rhs.first);
}
private: // "Настоящая" функция
bool keyLess(const Data::first_type& k1, // сравнения
const Data::first_type& k2) const {
return k1 < k2;
}
}
В данном примере предполагается, что сортированный вектор эмулирует map
. Перед нами практически буквальное переложение комментариев, приведенных ранее, если не считать присутствия функции keyLess
, предназначенной для согласования функций operator
. Каждая функция просто сравнивает два ключа, поэтому, чтобы не программировать одни и те же действия дважды, мы производим проверку в keyLess
, а функция operator
возвращает полученный результат. Конечно, этот прием упрощает сопровождение DataCompare
, однако у него есть один недостаток: наличие функций operator
с разными типами параметров исключает адаптацию объектов функций (см. совет 40). С этим ничего не поделаешь.
Контейнер map
эмулируется на базе сортированного вектора практически так же, как и контейнер set
. Единственное принципиальное отличие заключается в том, что в качестве функций сравнения используются объекты DataCompare
:
vector
… // Подготовительная фаза: много вставок,
// мало операций поиска
sort(vd.begin, vd.end, DataCompare); // Конец подготовительной фазы
// (при эмуляции multiset можно
// воспользоваться алгоритмом
// stable_sort - см. совет 31)
string s; // Объект с искомым значением
… // Начало фазы поиска
if (binary_search(vd.begin, vd.end, s, DataCompare))… // Поиск
// с применением binary_search
vector::iterator i = lower_bound(vd.begin, vd.end.s,
DataCompare); // Поиск с применением
if (i != vd.end && !(i->first < s))… // lower_bound: конструкция
//!(i->first
//в совете 45
pair
equal_range(vd.begin, vd.end, s, DataCompare); // Поиск с применением
if (range.first != range.second)… // equal_range
… // Конец фазы поиска,
// начало фазы реорганизации
sort(vd.begin, vd.end, DataCompare); //Начало новой фазы поиска…
Как видите, после написания DataCompare
все более или менее становится на свои места. Показанное решение часто быстрее работает и расходует меньше памяти, чем аналогичная архитектура с настоящим контейнером map
— при условии, что операции со структурой данных в вашей программе делятся на фазы, описанные на с. 99. Если подобное деление на фазы не соблюдается, использование сортированного вектора вместо стандартных ассоциативных контейнеров почти всегда оборачивается напрасной тратой времени.
Совет 24. Тщательно выбирайте между map::operator[] и map::insert
Допустим, у нас имеется класс Widget
с конструктором по умолчанию, а также конструктором и оператором присваивания с операндом типа double:
class Widget {
public:
Widget;
Widget(double weight);
Widget& operator=(double weight);
…
};