Совет 21. Следите за тем, чтобы функции сравнения возвращали false в случае равенства
Совет 21. Следите за тем, чтобы функции сравнения возвращали false в случае равенства
Сейчас я покажу вам нечто любопытное. Создайте контейнер set с типом сравнения less_equal и вставьте в него число 10:
set<int,less_equal<int> > s; // Контейнер s сортируется по критерию "<="
s.insert(10); // Вставка числа 10
Теперь попробуйте вставить число 10 повторно:
s.insert(10);
При этом вызове insert контейнер должен выяснить, присутствует ли в нем число 10. Мы знаем, что такое число уже есть, но контейнер глуп как пробка и все проверяет лично. Чтобы вам было проще понять, что при этом происходит, назовем первоначально вставленный экземпляр 10А, а новый экземпляр — 10в.
Контейнер перебирает свои внутренние структуры данных и ищет место для вставки 10в. В итоге ему придется проверить 10А и сравнить его с 10в. Для ассоциативного контейнера «сравнение» сводится к проверке эквивалентности (см. совет 19), поэтому контейнер проверяет эквивалентность объектов 10А и 10в. Естественно, при этой проверке используется функция сравнения контейнера set; в нашем примере это функция operator<=, поскольку мы задали функцию сравнения less_equal, a less_equal означает operator<=. Затем контейнер проверяет истинность следующего выражения:
!(10a<=10b)&&!(10b<=10a) // Проверка эквивалентности 10A и 10в
Оба значения, 10А и 10в, равны 10, поэтому условие 10А<=10В заведомо истинно. Аналогично истинно и условие 10В<=10А. Приведенное выше выражение упрощается до !(true) &&!(true), то есть false && false — результат равен false. Другими словами, контейнер приходит к выводу, что 10А и 10в не эквивалентны, и вставляет 10в в контейнер наряду с 10А. С технической точки зрения эта попытка приводит к непредсказуемым последствиям, но на практике в контейнере set появляются два экземпляра значения 10, а это означает утрату одного из важнейших свойств set. Передача типа сравнения less_equal привела к порче контейнера! Более того, любая функция сравнения, которая возвращает true для равных значений, приведет к тем же последствиям. Равные значения по определению не эквивалентны! Здорово, не правда ли?
Мораль: всегда следите за тем, чтобы функции сравнения для ассоциативных контейнеров возвращали false для равных значений. Будьте внимательны, поскольку это ограничение очень легко упустить из виду.
Например, в совете 20 рассказано о том, как написать функцию сравнения для контейнеров указателей string* обеспечивающую автоматическую сортировку содержимого контейнера по значениям строк, а не указателей. Приведенная функция сравнения сортирует строки по возрастанию, но давайте предположим, что вам понадобилась функция для сортировки по убыванию. Естественно, вы возьмете существующий код и отредактируете его. Но если не проявить достаточной осторожности, у вас может получиться следующий результат (изменения выделены жирным шрифтом):
struct StringPtrGreater:
public binary_function<const string*, // Жирным шрифтом выделены
const string*, // изменения, внесенные в код bool> {// из совета 20.
// Внимание - приведенное решение
// не работает!
bool operator()(const string *psl. const string *ps2) const
{
return !(*psl<*ps2); // Простое логическое отрицание
}// старого условия не работает!
};
Идея заключается в том, чтобы изменить порядок сортировки логическим отрицанием условия в функции сравнения. К сожалению, отрицанием операции «<» является не «>», а «>=», а мы выяснили, что операция «>=», возвращающая true для равных значений, не подходит для функции сравнения в ассоциативных контейнерах.
Правильный тип сравнения должен выглядеть так:
struct StringPtrGreater: // Правильный тип сравнения
public binary_function<const string*, // для ассоциативных контейнеров
const string*,
bool> {
bool operator() (const string *psl, const string *ps2) const {
return *ps2<*psl:// Поменять местами операнды
}
}:
Чтобы не попасть в ловушку, достаточно запомнить, что возвращаемое значение функции сравнения указывает, должно ли одно значение предшествовать другому в порядке сортировки, определяемом этой функцией. Равные значения никогда не предшествуют друг другу, поэтому функция сравнения всегда должна возвращать для них false.
Я знаю, о чем вы думаете. «Конечно, это имеет смысл для set и map, поскольку эти контейнеры не могут содержать дубликатов. А как насчет multiset и multimap? Раз эти контейнеры могут содержать дубликаты, так ли важно, что два объекта с одинаковыми значениями окажутся не эквивалентными? Сохраним оба, для этого и нужны mult-контейнеры. Верно?»
Нет, неверно. Давайте вернемся к исходному примеру, но на этот раз воспользуемся контейнером multiset:
multiset<int.less_equal<int> > s; // s сортируется по критерию "<="
s.insert(10):// Вставка числа 10А
s.insert(10);// Вставка числа 10в
Теперь s содержит два экземпляра числа 10, и было бы логично предположить, что при вызове equal _range мы получим пару итераторов, описывающих интервал с обеими копиями. Однако это невозможно. Функция equal_range, несмотря на свое название, определяет интервал не равных, а эквивалентных значений. В нашем примере функция сравнения s говорит, что 10А и 10в не эквивалентны, поэтому они не могут оказаться в интервале, определяемом функцией equal range.
Ну что, убедились? Функция сравнения всегда должна возвращать false для равных величин, в противном случае нарушается работа всех стандартных ассоциативных контейнеров (независимо от того, могут они содержать дубликаты или нет).
Строго говоря, функции сравнения, используемые для сортировки ассоциативных контейнеров, должны определять для сравниваемых объектов порядок строгой квазиупорядоченности (strict weak ordering); аналогичное ограничение действует и для функций сравнения, передаваемых алгоритмам, — таким, как sort (см. совет 31). Если вас заинтересуют подробности того, что понимается под строгой квазиупорядоченностью, информацию можно найти во многих серьезных руководствах по STL, в том числе «The С++ Standard Library» [3], «Generic Programming аnd the STL» [4] и на web-сайте SGI, посвященном STL [21]. Особых откровений не ждите, но одно из требований строгой квазиупорядоченности относится непосредственно к данному совету. Требование заключается в следующем: функция, определяющая строгую квазиупорядоченность, должна возвращать false при получении двух копий одного значения.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
false()
false() Функция false возвращает ложь. Она применяется следующим образом:boolean false()В XPath не определены логические константы, поэтому для того чтобы присвоить переменной значение false, нужно прибегнуть к функции false. (С переменными вы познакомитесь в главе
6.4. Ни в коем случае не reflow!
6.4. Ни в коем случае не reflow! В CSS-движке браузеров существует несколько операций, затрагивающих изменение картинки на экране браузера. В предыдущих тестах было рассмотрено начальное создание документа и способы ускорения это процесса. Однако все вышеприведенные советы в
Функции сравнения
Функции сравнения strcmpСравнивает строки.Синтаксис:int strcmp(string str1, string str2)Эта функция сравнивает две строки посимвольно (точнее, бобайтово) и возвращает:Так как сравнение идет побайтово, то регистр символов влияет на результаты сравнений.strncmpСравнивает начала
Совет 4. Вызывайте empty вместо сравнения size() с нулем
Совет 4. Вызывайте empty вместо сравнения size() с нулем Для произвольного контейнера с следующие две команды фактически эквивалентны:if (c.size()==0)...if (c.empty())...Возникает вопрос — почему же предпочтение отдается одной конструкции, особенно если учесть, что empty обычно реализуется в
Совет 5. Используйте интервальные функции вместо одноэлементных
Совет 5. Используйте интервальные функции вместо одноэлементных Есть два вектора, v1 и v2. Как проще всего заполнить v1 содержимым второй половины v2? Только не надо мучительно размышлять над тем, что считать «половиной» при нечетном количестве элементов в v2. Просто
Совет 20. Определите тип сравнения для ассоциативного контейнера, содержащего указатели
Совет 20. Определите тип сравнения для ассоциативного контейнера, содержащего указатели Предположим, у нас имеется контейнер set, содержащий указатели string*, и мы пытаемся включить в него несколько новых элементов:set<string*> ssp; // ssp = "set of string ptrs"ssp.insert(new string("Anteater"));ssp.insert(new
Совет 30. Следите за тем, чтобы приемный интервал имел достаточный размер
Совет 30. Следите за тем, чтобы приемный интервал имел достаточный размер Контейнеры STL автоматически увеличиваются с добавлением новых объектов (функциями insert, push_front, push_back и т. д.). Автоматическое изменение размеров чрезвычайно удобно, и у многих программистов создается
Совет 35. Реализуйте простые сравнения строк без учета регистра символов с использованием mismatch или lexicographical_compare
Совет 35. Реализуйте простые сравнения строк без учета регистра символов с использованием mismatch или lexicographical_compare Один из вопросов, часто задаваемых новичками в STL — «Как в STL сравниваются строки без учета регистра символов?» Простота этого вопроса обманчива. Сравнения
Совет 42. Следите за тем, чтобы конструкция less<T> означала operator<
Совет 42. Следите за тем, чтобы конструкция less<T> означала operator< Допустим, объект класса Widget обладает атрибутами weight и maxSpeed:class Widget { public:size_t weight() const;size_t maxSpeed() const;}Будем считать, что естественная сортировка объектов Widget осуществляется по атрибуту weight, что отражено в
Совет 44. Используйте функции контейнеров вместо одноименных алгоритмов
Совет 44. Используйте функции контейнеров вместо одноименных алгоритмов Некоторые контейнеры содержат функции, имена которых совпадают с именами алгоритмов STL. Так, в ассоциативных контейнерах существуют функции count, find, lower_bound, upper_bound и equal_range, а в контейнере list
Следите за логами
Следите за логами Изучайте свои логи, чтобы следить за дискуссиямиВам полезно знать, кто о вас говорит. Проверьте свои логи и узнайте, что откуда берется. Кто на вас ссылается? Кто ругается? Какие из блогов, попавшие в списки на Technorati, Blogdex, Feedster, Del.icio.us и Daypop, говорят о
Функции true и false
Функции true и false boolean true()boolean false()Две функции true и false возвращают тождественную "истину" и тождественную "ложь" соответственно. В XPath нет констант и, тем более, логических констант, определяющих "истину" и "ложь", как в других языках. Функции true и false восполняют эту
7.7 Операции Равенства
7.7 Операции Равенства выражение_равенства: выражение == выражение выражение != выражениеОперации == и != в точности аналогичны операциям сравнния за исключением их низкого приоритета. (Так, a«b == c«d есть 1 всегда, когда a«b и c«d имеют одинаковое истинностное значение.)Указатель
Реагирование в случае нарушения доверия
Реагирование в случае нарушения доверия Политика доверия должна предлагать некоторые финансовые гарантии, то есть страховать пользователя, на тот случай, когда невозможно обеспечить полную защиту его ресурсов. Достаточно часто в политиках содержится утверждение о