87. Делайте предикаты чистыми функциями
87. Делайте предикаты чистыми функциями
Резюме
Предикат представляет собой функциональный объект, который возвращает ответ да/нет, обычно в виде значения типа bool. Функция является "чистой" в математическом смысле, если ее результат зависит только от ее аргументов (обратите внимание — в данном случае термин "чистая" не имеет никакого отношения к чисто виртуальным функциям).
Не позволяйте предикатам сохранять или обращаться к состоянию так, чтобы это могло влиять на результат работы оператора operator(); при этом понятие состояния включает как данные-члены, так и глобальные состояния. Для предикатов желательно делать оператор operator() константной функцией-членом (см. рекомендацию 15).
Обсуждение
Алгоритмы создают неизвестное количество копий предикатов в неизвестные моменты времени и в неизвестном порядке, так что приходится полагаться на то, что все копии эквивалентны.
Именно поэтому вы отвечаете за то, чтобы все копии предикатов были эквивалентны; это означает, что все они должны быть чистыми функциями, результат работы которых полностью и однозначно определяется аргументами, передаваемыми оператору operator() и не зависит ни от каких иных факторов. При передаче одних и тех же аргументов предикат всегда должен возвращать одно и то же значение.
Предикаты с состояниями могут показаться полезными, но они явно не очень полезны при использовании с алгоритмами стандартной библиотеки С++, и это сделано преднамеренно. В частности, предикаты с состояниями могут быть полезны только при выполнении ряда условий.
• Предикат не копируется. Стандартные алгоритмы не дают такой гарантии; в действительности алгоритмы, напротив, предполагают, что предикаты могут безопасно копироваться.
• Предикаты используются в предопределенном документированном порядке. В общем случае стандартные алгоритмы не дают никакой гарантии относительно порядка применения предикатов к элементам диапазона. При отсутствии гарантий по поводу порядка обработки элементов, операция наподобие "пометить третий элемент" (см. примеры) имеет мало смысла, поскольку не определено, какой именно элемент будет обработан третьим.
Первое условие можно обойти, написав предикат с использованием счетчика ссылок. Этот метод решает проблему копирования предикатов, поскольку в таком случае предикаты могут безопасно копироваться без изменения их семантики при применении к объектам (см. [Sutter02]). Однако обойти второе условие оказывается невозможно.
Всегда объявляйте оператор предиката operator() как константную функцию-член, чтобы компилятор мог помочь вам избежать неприятностей, выводя сообщение об ошибке при попытках изменить любые данные-члены, которые могут быть у предиката. Это не позволяет пресечь все злоупотребления, например, доступ к глобальным данным, но, по крайней мере, поможет избежать наиболее распространенных ошибок.
Примеры
Пример. FlagNth. Перед вами классический пример из [Sutter02], в котором выполняется попытка удалить третий элемент из контейнера v.
class FlagNth {
public:
FlagNth(size_t n) : current_(0), n_(n) { }
// Возвращаем значение true только при третьем вызове
template<typename T>
bool operator()(const T&) // Плохо: неконстантная
{ return ++current_ == n_; } // функция
private:
size_t current_, n_;
};
// ... позже ...
v.erase(remove_if(v.begin(), v.end(), FlagNth(3)));
Увы, нет никакой гарантии, что будет удален именно третий элемент В большинстве реальных реализаций STL приведенный код наряду с третьим удалит и шестой элемент. Почему? Потому что remove_if обычно реализуется с использованием find_if и remove_copy_if, и копия предиката передается каждой из этих функций.
Концептуально этот пример неверен, поскольку алгоритм remove_if гарантирует только то, что он удалит все элементы, удовлетворяющие некоторому критерию. Он не документирует порядок, в котором совершается обход или удаление элементов из обрабатываемого диапазона, так что приведенный код использует предположение, которое не документировано и, более того, не выполняется.
Корректный способ удаления третьего элемента — выполнить итерации для его поиска и вызвать функцию erase.
Ссылки
[Austern99] §4.2.2 • [Josuttis99] §5.8.2, §8.1.4 • [Meyers01] §39 • [Stroustrup00] §10.2.6 • [Sutter02] §2-3
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Принцип 7: Делайте упор на конверсию
Принцип 7: Делайте упор на конверсию Давайте совершим небольшое путешествие во времени. в март 2000 года. В журнале Fortune появляется статья, в которой Билл Керли, один из ведущих интернет-аналитиков, сколотивший состояние на вложениях в венчурный бизнес, пишет, что
§ 57. Делайте сайты проще
§ 57. Делайте сайты проще Простота — необходимое условие прекрасного. Л. Н. Толстой. Из письма к Л. Андрееву от 02.09.1902 2 августа 2000Почти сто лет прошло с момента написания эпиграфа к этому параграфу, а ценность высказывания не уменьшилась.Желание усложнить всегда
Стойкое дежавю: не делайте регистрацию обязательной
Стойкое дежавю: не делайте регистрацию обязательной 30 % посетителей интернет-магазинов уходят с сайта лишь потому, что им нужно было зарегистрироваться, чтобы совершить покупку. Пользователи не любят регистрацию, так как у них уже есть уйма разных аккаунтов и им не
Глава 10 Работа с формулами и функциями
Глава 10 Работа с формулами и функциями Программа Excel предоставляет широкие возможности для использования формул. Как уже упоминалось в прошлой главе, формулы – это выражения, состоящие из числовых величин, адресов ячеек и функций, соединенных знаками арифметических
23. Делайте заголовочные файлы самодостаточными
23. Делайте заголовочные файлы самодостаточными РезюмеУбедитесь, что каждый написанный вами заголовочный файл компилируем самостоятельно, т.е. что он включает все заголовочные файлы, от которых зависит его содержимое.ОбсуждениеЕсли один заголовочный файл не работает,
9. Как правильно пользоваться функциями
9. Как правильно пользоваться функциями ФУНКЦИИСТРОИТЕЛЬНЫЕ БЛОКИ ПРОГРАММЫСВЯЗЬ МЕЖДУ ФУНКЦИЯМИ: АРГУМЕНТЫ, УКАЗАТЕЛИ, ВОЗВРАТ ЗНАЧЕНИЯТИПЫ
Делайте меньше
Делайте меньше Меньше соревнуйтесь Народная мудрость гласит: чтобы подавить своих конкурентов, вы должны переиграть их. Если у них четыре возможности, у вас их должно быть пять, пятнадцать, двадцать… Если они тратят N, то вы должны потратить NN. Если у них есть 20, то у вас
Делайте идейное программное обеспечение
Делайте идейное программное обеспечение Ваше приложение должно лавировать между потребностямиНекоторые люди считают, что программное обеспечение должно быть агностическим. Они говорят, что самоуверенно для разработчиков ограничивать особенности или пренебрегать
22.1. Сложные функции и сложности с функциями
22.1. Сложные функции и сложности с функциями Функции могут принимать входные аргументы и возвращать код завершения.function_name $arg1 $arg2Доступ к входным аргументам, в функциях, производится посредством позиционных параметров, т.е. $1, $2 и так
Видеоплаты с функциями видеозахвата
Видеоплаты с функциями видеозахвата Некоторые фирмы, производящие видеоплаты (например, фирма Matrox), создали свои модули захвата кадров, работающие только с данным видеоадаптером. Несколько лет назад такое решение было довольно распространенным, однако сейчас подобные
Повышайте качество - делайте меньше за спринт!
Повышайте качество - делайте меньше за спринт! Это решается ещё на планировании спринта. Проще говоря, не пытайтесь сделать как можно больше историй за спринт. Если у вас существуют проблемы с качеством или вам приходится тратить слишком много времени на приёмочное
6.2.7. Работаем с функциями
6.2.7. Работаем с функциями Для выполнения задания нам понадобится материал разд. 5.2.7.Задание? Создать таблицу, как показано на рис. 6.34.? В ячейки диапазона F6: F13 записать формулы для подсчета суммы набранных баллов.? В ячейки G6:G13 внести формулы для отображения отметок о