Чтение онлайн

на главную - закладки

Жанры

Эффективное использование STL
Шрифт:

При этом вызове

insert
контейнер должен выяснить, присутствует ли в нем число 10. Мы знаем, что такое число уже есть, но контейнер глуп как пробка и все проверяет лично. Чтобы вам было проще понять, что при этом происходит, назовем первоначально вставленный экземпляр 10A, а новый экземпляр — 10B.

Контейнер перебирает свои внутренние структуры данных и ищет место для вставки 10B. В итоге ему придется проверить 10A и сравнить его с 10B. Для ассоциативного контейнера «сравнение» сводится к проверке эквивалентности (см. совет 19), поэтому контейнер проверяет эквивалентность объектов 10A и 10B.

Естественно, при этой проверке используется функция сравнения контейнера
set
; в нашем примере это функция
operator<=
, поскольку мы задали функцию сравнения
less_equal
, a
less_equal
означает
operator<=
. Затем контейнер проверяет истинность следующего выражения:

!(10a<=10b)&&!(10b<=10a) // Проверка эквивалентности 10A и 10B

Оба значения, 10A и 10B, равны 10, поэтому условие 10A<=10B заведомо истинно. Аналогично истинно и условие 10B<=10A. Приведенное выше выражение упрощается до

!(true)&&!(true)
, то есть
false&&false
— результат равен
false
. Другими словами, контейнер приходит к выводу, что 10A и 10B не эквивалентны, и вставляет 10B в контейнер наряду с 10A. С технической точки зрения эта попытка приводит к непредсказуемым последствиям, но на практике в контейнере
set
появляются два экземпляра значения 10, а это означает утрату одного из важнейших свойств
set
. Передача типа сравнения
less_equal
привела к порче контейнера! Более того, любаяфункция сравнения, которая возвращает
true
для равных значений, приведет к тем же последствиям. Равные значения по определению неэквивалентны! Здорово, не правда ли?

Мораль: всегда следите за тем, чтобы функции сравнения для ассоциативных контейнеров возвращали

false
для равных значений. Будьте внимательны, поскольку это ограничение очень легко упустить из виду.

Например, в совете 20 рассказано о том, как написать функцию сравнения для контейнеров указателей

string*
обеспечивающую автоматическую сортировку содержимого контейнера по значениям строк, а не указателей. Приведенная функция сравнения сортирует строки по возрастанию, но давайте предположим, что вам понадобилась функция для сортировки по убыванию. Естественно, вы возьмете существующий код и отредактируете его. Но если не проявить достаточной осторожности, у вас может получиться следующий результат (изменения выделены жирным шрифтом):

struct StringPtrGreater:

 public binary_function<const string*, // Жирным шрифтом выделены

 const string*, // изменения, внесенные в код

 bool> { // из совета 20.

// Внимание - приведенное решение

// не работает!

 bool operator(const string *ps1, const string *ps2) const {

return !(*ps1 < *ps2); // Простое логическое отрицание

 } // старого условия не работает!

};

Идея заключается в том, чтобы изменить порядок сортировки логическим отрицанием условия в функции сравнения. К сожалению, отрицанием операции «

<
» является не «
>
», а «
>=
», а мы выяснили, что операция «
>=
», возвращающая
true
для равных значений, не подходит для функции сравнения в ассоциативных контейнерах.

Правильный тип сравнения должен выглядеть так:

struct StringPtrGreater: // Правильный тип сравнения

 public binary_function<const string*, // для ассоциативных контейнеров

 const string*, bool> {

 bool operator (const string *ps1, const string *ps2) const {

return *ps2<*ps1;// Поменять местами операнды

 }

};

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

false
.

Я знаю, о чем вы думаете. «Конечно, это имеет смысл для

set
и
map
, поскольку эти контейнеры не могут содержать дубликатов. А как насчет
multiset
и
multimap
? Раз эти контейнеры могут содержать дубликаты, так ли важно, что два объекта с одинаковыми значениями окажутся не эквивалентными? Сохраним оба, для этого и нужны mult-контейнеры. Верно?»

Нет, неверно. Давайте вернемся к исходному примеру, но на этот раз воспользуемся контейнером

multiset
:

multiset<int, less_equal<int> > s; // s сортируется по критерию "<="

s.insert(10);// Вставка числа 10A

s.insert(10);// Вставка числа 10B

Теперь

s
содержит два экземпляра числа 10, и было бы логично предположить, что при вызове
equal_range
мы получим пару итераторов, описывающих интервал с обеими копиями. Однако это невозможно. Функция
equal_range
, несмотря на свое название, определяет интервал не равных, а эквивалентныхзначений. В нашем примере функция сравнения
s
говорит, что 10A и 10B не эквивалентны, поэтому они не могут оказаться в интервале, определяемом функцией
equal_range
.

Ну что, убедились? Функция сравнения всегда должна возвращать false для равных величин, в противном случае нарушается работа всех стандартных ассоциативных контейнеров (независимо от того, могут они содержать дубликаты или нет).

Строго говоря, функции сравнения, используемые для сортировки ассоциативных контейнеров, должны определять для сравниваемых объектов порядок строгой квазиупорядоченности (strict weak ordering); аналогичное ограничение действует и для функций сравнения, передаваемых алгоритмам, — таким, как

sort
(см. совет 31). Если вас заинтересуют подробности того, что понимается под строгой квазиупорядоченностью, информацию можно найти во многих серьезных руководствах по STL, в том числе «The C++ Standard Library» [3], «Generic Programming аnd the STL» [4] и на web-сайте SGI, посвященном STL [21]. Особых откровений не ждите, но одно из требований строгой квазиупорядоченности относится непосредственно к данному совету. Требование заключается в следующем: функция, определяющая строгую квазиупорядоченность, должна возвращать
false
при получении двух копий одного значения.

Совет 22. Избегайте изменения ключа «на месте» в контейнерах set и multiset

Понять смысл этого совета нетрудно. Контейнеры

set/multiset
, как и все стандартные ассоциативные контейнеры, хранят свои элементы в отсортированном порядке, и правильное поведение этих контейнеров зависит от сохранения этого порядка. Если изменить значение элемента в ассоциативном контейнере (например заменить 10 на 1000), новое значение окажется в неправильной позиции, что нарушит порядок сортировки элементов в контейнере.

Поделиться:
Популярные книги

Как я строил магическую империю 4

Зубов Константин
4. Как я строил магическую империю
Фантастика:
боевая фантастика
постапокалипсис
аниме
фантастика: прочее
фэнтези
5.00
рейтинг книги
Как я строил магическую империю 4

Безумный Макс. Поручик Империи

Ланцов Михаил Алексеевич
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Безумный Макс. Поручик Империи

Попаданка 3

Ахминеева Нина
3. Двойная звезда
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Попаданка 3

Муж на сдачу

Зика Натаэль
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Муж на сдачу

Призыватель нулевого ранга. Том 3

Дубов Дмитрий
3. Эпоха Гардара
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Призыватель нулевого ранга. Том 3

На границе империй. Том 10. Часть 5

INDIGO
23. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 5

Адвокат

Константинов Андрей Дмитриевич
1. Бандитский Петербург
Детективы:
боевики
8.00
рейтинг книги
Адвокат

На границе империй. Том 7

INDIGO
7. Фортуна дама переменчивая
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
6.75
рейтинг книги
На границе империй. Том 7

Здравствуй, 1985-й

Иванов Дмитрий
2. Девяностые
Фантастика:
альтернативная история
5.25
рейтинг книги
Здравствуй, 1985-й

О, Путник!

Арбеков Александр Анатольевич
1. Квинтет. Миры
Фантастика:
социально-философская фантастика
5.00
рейтинг книги
О, Путник!

Чужбина

Седой Василий
2. Дворянская кровь
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Чужбина

Бестужев. Служба Государевой Безопасности. Книга четвертая

Измайлов Сергей
4. Граф Бестужев
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности. Книга четвертая

Локки 5. Потомок бога

Решетов Евгений Валерьевич
5. Локки
Фантастика:
юмористическое фэнтези
аниме
фэнтези
5.00
рейтинг книги
Локки 5. Потомок бога

На границе империй. Том 10. Часть 4

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 4