Читать «Стандарты программирования на С++. 101 правило и рекомендация» онлайн - страница 139
Андрей Александреску
template<typename Iter, typename Compare>
Iter find_if(Iter first, Iter last, Compare comp);
Если мы передадим алгоритму в качестве компаратора функцию
inline bool Function(const Thing&) { /* ... */ }
find_if(v.begin(), v.end(), Function);
то на самом деле будет передана ссылка на функцию. Компиляторы редко встраивают вызовы таких функций (за исключением некоторых относительно свежих компиляторов, которые в состоянии провести анализ всей программы в целом), даже если они объявлены как таковые и видимы в момент компиляции вызова find_if
. Кроме того, как уже упоминалось, функции не адаптируемы.
Давайте передадим алгоритму find_if
в качестве компаратора функциональный объект:
struct FunctionObject : unary_function<Thing, bool> {
bool operator()(const Thing&) const { /* ... */ }
};
find_if(v.begin(), v.end(), FunctionObject());
Если мы передаем объект, который имеет (явно или неявно) встраиваемый оператор operator()
, то такие вызовы компиляторы С++ способны делать встраиваемыми уже очень давно.
Примечание. Эта методика не является преждевременной оптимизацией (см. рекомендацию 8); ее следует рассматривать как препятствие преждевременной пессимизации (см. рекомендацию 9). Если у вас имеется готовая функция — передавайте указатель на нее (кроме тех ситуаций, когда вы должны обязательно обернуть ее в ptr_fun
или mem_fun
). Но если вы пишете новый код для использования в качестве аргумента алгоритма, то лучше сделать его функциональным объектом.
Ссылки
89. Корректно пишите функциональные объекты
Резюме
Разрабатывайте функциональные объекты так, чтобы их копирование выполнялось как можно эффективнее. Там, где это возможно, делайте их максимально адаптируемыми путем наследования от unary_function
или binary_function
.
Обсуждение
Функциональные объекты моделируют указатели на функции. Подобно указателям на функции, они обычно передаются в функции по значению. Все стандартные алгоритмы передают объекты по значению, и то же должны делать и ваши алгоритмы, например:
template<class InputIter, class Func>
Function for_each(InputIter first, InputIter last, Function f);
Следовательно, функциональные объекты должны легко копироваться и быть мономорфными (для защиты от срезки), так что избегайте виртуальных функций (см. рекомендацию 54). Конечно, у вас могут быть большие и/или полиморфные функциональные объекты — их тоже вполне можно использовать; просто скройте их размер с помощью идиомы Pimpl (указателя на реализацию; см. рекомендацию 43). Эта идиома позволяет, как и требуется, получить внешний мономорфный класс малого размера, обеспечивающий доступ к богатой функциональности. Внешний класс должен удовлетворять следующим условиям.
• unary_function
или binary_function
.
• shared_ptr
) на (возможно, большого размера) реализацию необходимой функциональности.