Ни С, ни С++ не позволяют передавать функции в качестве параметров других функций. Вместо этого разрешается передавать
void qsort(void *base, size_t nmemb, size_t size,
int (*cmpfcn)(const void*,const void*));
В совете 46 объясняется, почему вместо функции qsort обычно рекомендуется использовать алгоритм sort, но дело не в этом. Нас сейчас интересует объявление параметра cmpfcn
функции qsort
. При внимательном анализе становится ясно, что аргумент cmpcfn
, который является указателем на функцию, копируется (то есть передается по значению) из точки вызова в функцию qsort. Данный пример поясняет правило, соблюдаемое стандартными библиотеками С и С++, — указатели на функции должны передаваться по значению.
Объекты функций STL создавались по образцу указателей на функции, поэтому в STL также действует правило, согласно которому объекты функций передаются по значению (то есть копируются). Вероятно, это правило лучше всего демонстрирует приведенное в Стандарте объявление алгоритма for_each, который получает и передает по значению объекты функций:
template
class Function>
Functon // Возврат по значению
for_each(InputIterator first,
InputIterator last,
Functon f);// Передача по значению
Честно говоря, передача по значению не гарантирована полностью, поскольку вызывающая сторона может явно задать типы параметров в точке вызова. Например, в следующем фрагменте foreach получает и возвращает функторы по ссылке:
class DoSomething:
public unary_function
void operator() (int x){...}// в совете 40
};
typedef deque
deque
...
DoSomething d; // Создать объект функции
for_each
DoSomethng&>(di .begin(),//параметров DequelntIter
di.end(),//и DoSomething&: в результате
d);//происходит передача
//и возврат по ссылке.
Пользователи STL почти никогда не используют эту возможность, а в некоторых реализациях алгоритмов STL при передаче объектов функций по ссылке программы даже не компилируются. В продолжение этого совета будем считать, что объекты функций всегда передаются по значению, поскольку на практике это почти всегда так.
Поскольку объекты функций передаются и возвращаются по значению, вы должны позаботиться о том, чтобы объект функции правильно работал при передаче подобным способом (то есть копированием). Для этого необходимо соблюдение двух условий. Во-первых, объекты функций должны быть небольшими, в противном случае копирование обойдется слишком дорого. Во-вторых, объекты функций должны быть
Бесспорно, эффективность является важным фактором, и предотвратить отсечение тоже необходимо, однако не все функторы малы и мономорфны. Одно из преимуществ объектов функций перед обычными функциями заключается в отсутствии ограничений на объем информации состояния. Некоторые объекты функций от природы «упитанны», и очень важно, чтобы они могли передаваться алгоритмам STL так же просто, как и их «тощие» собратья.
Столь же нереалистичен и запрет на полиморфные функторы. Иерархическое наследование и динамическое связывание относятся к числу важнейших особенностей С++, и при проектировании классов функторов они могут принести такую же пользу, как и в других областях. Что такое классы функторов без наследования? С++ без «++». Итак, необходимы средства, которые бы позволяли легко передавать большие и/или полиморфные объекты функций с соблюдением установленного в STL правила о передаче функторов по значению.
Такие средства действительно существуют. Достаточно взять данные и/или полиморфные составляющие, которые требуется сохранить в классе функтора, перенести их в другой класс и сохранить в классе функтора указатель на этот новый класс. Рассмотрим пример создания класса полиморфного функтора с большим количеством данных:
template
class BPFC: //Functor class"
public // Базовый класс описан