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

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

Жанры

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

Эквивалентность основана на относительном порядке значений объектов в отсортированном интервале. Проще всего рассматривать ее в контексте порядка сортировки, являющегося частью любого стандартного ассоциативного контейнера (то есть

set
,
multiset
,
map
и
multimap
). Два объекта 
x
и 
y
считаются эквивалентными по отношению к порядку сортировки, используемому ассоциативным контейнером
c
, если ни один из них не предшествует другому в порядке сортировки
c
. На первый взгляд такая формулировка кажется запутанной, но на практике все просто. Возьмем контейнер
set<Widget> s
. Два объекта
Widget
,
w1
и
w2
,
имеют эквивалентные значения по отношению к
s
, если ни один из них не предшествует другому в порядке сортировки
s
. Стандартная функция сравнения для
set<Widget>
less<Widget>
— по умолчанию просто вызывает
operator<
для объектов
Widget
, поэтому
w1
и
w2
будут иметь значения, эквивалентные по отношению к
operator<
если истинно следующее выражение:

!(w1<w2) // Условие w1 < w2 ложно

&& // и

!(w2<w1)// Условие w2 < w1 ложно

Все вполне логично: два значения эквивалентны (по отношению к некоторому критерию упорядочения), если ни одно из них не предшествует другому в соответствии с данным критерием.

В общем случае функцией сравнения для ассоциативного контейнера является не оператор

<
или даже
less
, а пользовательский предикат (см. совет 39). Каждый стандартный ассоциативный контейнер предоставляет свой предикат сортировки через функцию
key_comp
, поэтому два объекта 
x
и 
y
имеют эквивалентные значения по отношению к критерию сортировки ассоциативного контейнера
c
, если выполняется следующее условие:

!c.key_comp(x, y) && !c.key_comp(y, x) // х не предшествует у

// в порядке сортировки с,

// а у не предшествует х

Выражение

!c.key_comp(x,y)
выглядит устрашающе, но стоит понять, что
c.key_comp
возвращает функцию (или объект функции), как все затруднения исчезают. Перед нами простой вызов функции (или объекта функции), возвращаемой
key_comp
, которой передаются аргументы 
x
и
y
. Затем вычисляется логическое отрицание результата. Функция
с.keycomp(х, у)
возвращает
true
лишь в том случае, если 
x
предшествует 
y
в порядке сортировки, поэтому выражение
!с.key_comp(х, у)
истинно только в том случае, если 
x
не предшествует 
y
в порядке сортировки
c
.

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

set<string>
без учета регистра символов, то есть контейнер
set<string>
, в котором функция сравнения игнорирует регистр символов в строках. С точки зрения такой функции строки «STL» и «stL» эквивалентны. Пример реализации функции
ciStringCompare
, игнорирующей регистр символов, приведен в совете 35, однако
set
требуется типфункции сравнения, а не сама функция. Чтобы заполнить этот пробел, мы пишем класс функтора с оператором
, вызывающим
ciStringCompare
:

struct CiStringCompare: // Класс сравнения строк

public // без учета регистра символов;

binary_function<string, string, bool>{ // описание базового класса

// приведено в совете 40

bool operator (const string& lhs, const string& rhs) const {

 return ciStringCompare(lhs, rhs); // Реализация ciStringCompare

// приведена в совете 35

 }

};

При наличии

CiStringCompare
контейнер
set<string>
,
игнорирующий регистр символов, создается очень просто:

set<string, CIStringCompare> ciss;

Теперь при вставке строк «Persephone» и «persephone» в множество будет включена только первая строка, поскольку вторая считается эквивалентной:

ciss.insert("Persephone"); // В множество включается новый элемент

ciss.insert("persephone"); // Эквивалентный элемент не включается

Если теперь провести поиск строки «persephone» функцией

set::find
, результат будет успешным:

if(ciss.find("persephone")!=ciss.end)... // Элемент найден

Но если воспользоваться внешним алгоритмом

find
, поиск завершается неудачей:

if (find(ciss.begin, ciss.end,

 "persephone")!=ciss.end)… // Элемент отсутствует

Дело в том, что строка «persephone» эквивалентна«Persephone» (по отношению к функтору сравнения

CIStringCompare
), но не равна ей (поскольку
string("persephone") !=string("Persephone")
). Приведенный пример поясняет одну из причин, по которой в соответствии с советом 44 рекомендуется использовать функции контейнеров (
set::find
) вместо их внешних аналогов (
find
).

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

Стандартные ассоциативные контейнеры сортируются, поэтому каждый контейнер должен иметь функцию сравнения (по умолчанию

less
), определяющую порядок сортировки. Эквивалентность определяется в контексте функции сравнения, поэтому клиентам стандартных ассоциативных контейнеров остается лишь задать единственную функцию сравнения. Если бы ассоциативные контейнеры при сравнении объектов использовали критерий равенства, то каждому ассоциативному контейнеру, помимо используемой при сортировке функции сравнения, пришлось бы определять вторую функцию для проверки равенства. Вероятно, по умолчанию функция сравнения вызывала бы
equal_to
, но интересно заметить, что функция
equal_to
в STL не используется в качестве стандартной функции сравнения. Когда в STL возникает необходимость проверки равенства, по умолчанию просто вызывается оператор
==
. В частности, именно так поступает внешний алгоритм
find
.

Допустим, у нас имеется контейнер

set2CF
, построенный по
образцу
set — «set с двумя функциями сравнения». Первая функция сравнения определяет порядок сортировки элементов множества, а вторая используется для проверки равенства. А теперь рассмотрим следующее объявление:

set2CF<string, CIStringCompare, equal_to<string> > s;

Контейнер

s
производит внутреннюю сортировку строк без учета регистра, но с использованием интуитивного критерия равенства: две строки считаются равными при совпадении их содержимого. Предположим, в
s
вставляются два варианта написания строки «Persephone»:

s.insert("Persephone");

s.insert("persephone");

Как поступить в этом случае? Если контейнер поймет, что

"Persephone" != "persephone"
, и вставит обе строки в
s
, в каком порядке они должны следовать?

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

multiset
и
multimap
, поскольку Стандарт не устанавливает никаких ограничений на относительный порядок следования эквивалентных значений (
multiset
) или ключей (
multimap
). Или нам следует настоять на детерминированном порядке содержимого
s
и проигнорировать вторую попытку вставки (для строки «persephone»)? А если будет выбран этот вариант, что произойдет при выполнении следующей команды:

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

Камень Книга седьмая

Минин Станислав
7. Камень
Фантастика:
фэнтези
боевая фантастика
6.22
рейтинг книги
Камень Книга седьмая

Я сделаю это сама

Кальк Салма
1. Магический XVIII век
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Я сделаю это сама

Кровь на эполетах

Дроздов Анатолий Федорович
3. Штуцер и тесак
Фантастика:
альтернативная история
7.60
рейтинг книги
Кровь на эполетах

Сыночек в награду. Подари мне любовь

Лесневская Вероника
1. Суровые отцы
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Сыночек в награду. Подари мне любовь

Кодекс Крови. Книга ХII

Борзых М.
12. РОС: Кодекс Крови
Фантастика:
боевая фантастика
попаданцы
5.00
рейтинг книги
Кодекс Крови. Книга ХII

Найди меня Шерхан

Тоцка Тала
3. Ямпольские-Демидовы
Любовные романы:
современные любовные романы
короткие любовные романы
7.70
рейтинг книги
Найди меня Шерхан

Идеальный мир для Лекаря 4

Сапфир Олег
4. Лекарь
Фантастика:
фэнтези
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 4

Релокант. По следам Ушедшего

Ascold Flow
3. Релокант в другой мир
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Релокант. По следам Ушедшего

Мир-о-творец

Ланцов Михаил Алексеевич
8. Помещик
Фантастика:
альтернативная история
5.00
рейтинг книги
Мир-о-творец

Протокол "Наследник"

Лисина Александра
1. Гибрид
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Протокол Наследник

Кодекс Охотника. Книга VII

Винокуров Юрий
7. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
4.75
рейтинг книги
Кодекс Охотника. Книга VII

Попаданка в семье драконов

Свадьбина Любовь
Попаданка в академии драконов
Любовные романы:
любовно-фантастические романы
7.37
рейтинг книги
Попаданка в семье драконов

Новые горизонты

Лисина Александра
5. Гибрид
Фантастика:
попаданцы
технофэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Новые горизонты

Скрываясь в тени

Мазуров Дмитрий
2. Теневой путь
Фантастика:
боевая фантастика
7.84
рейтинг книги
Скрываясь в тени