14.8. Оператор вызова функции
Классы, перегружающие оператор вызова, позволяют использовать объекты этого типа как функции. Поскольку объекты таких классов способны хранить состояние, они могут оказаться существенно гибче обычных функций.
В качестве простого примера рассмотрим структуру absInt, обладающую оператором вызова, возвращающим абсолютное значение своего аргумента:
struct absInt {
int operator()(int val) const {
return val < 0 ? -val : val;
}
};
Этот класс определяет одну функцию: оператор вызова функции. Этот оператор получает аргумент типа int и возвращает абсолютное значение аргумента.
Оператор вызова используется применительно к списку аргументов объекта класса absInt способом, который выглядит как вызов функции:
int i = -42;
absInt absObj; // объект класса с оператором вызова функции
int ui = absObj(i); // передача i в absObj.operator()
Хотя absObj — это объект, а не функция, его вполне можно вызвать. При вызове объект выполняет свой перегруженный оператор вызова. В данном случае этот оператор получает значение типа int и возвращает его абсолютное значение.

Объект класса, определяющего оператор вызова, называется объектом функции (function object). Такие объекты действуют как функции, поскольку их можно вызвать.
Классы объектов функций с состоянием
У класса объекта функции, как у любого другого класса, могут быть и другие члены, кроме оператора operator(). Классы объекта функции зачастую содержат переменные-члены, используемые для настройки действий в операторе вызова.
В качестве примера определим класс, выводящий строковый аргумент. По умолчанию класс будет писать в поток cout и выводить пробел после каждой строки. Позволим также пользователям класса предоставлять другой поток для записи и другой разделитель. Этот класс можно определить следующим образом:
class PrintString {
public:
PrintString(ostream &o = cout, char c = ' '):
os(o), sep(c) { }
void operator()(const string &s) const { os << s << sep; }
private:
ostream &os; // поток для записи
char sep; // символ завершения после каждого вывода
};
У класса есть конструктор, получающий ссылку на поток вывода, и символ, используемый как разделитель. Как аргументы по умолчанию (см. раздел 6.5.1) для этих параметров используется поток cout и пробел. Тело оператора вызова функции использует эти члены при выводе данной строки.
При определении объектов класса PrintString можно использовать аргументы по умолчанию или предоставлять собственные значения для разделителя или потока вывода:
PrintString printer; // использует аргументы по умолчанию; вывод в cout
printer(s); // выводит s и пробел в cout
PrintString errors(cerr, ' ');
errors(s); // выводит s и новую строку в cerr
Объекты функции обычно используют как аргументы для обобщенных алгоритмов. Например, для вывода содержимого контейнера можно использовать класс PrintString и библиотечный алгоритм for_each() (см. раздел 10.3.2):
for_each(vs.begin(), vs.end(), PrintString(cerr, ' '));
Третий аргумент алгоритма for_each() является временным объектом типа PrintString, инициализируемый потоком cerr и символом новой строки. Вызов функции for_each() выводит каждый элемент vs в поток cerr, разделяя их новой строкой.
Упражнения раздела 14.8
Упражнение 14.33. Сколько операндов может иметь перегруженный оператор вызова функции?
Упражнение 14.34. Определите класс объекта функции для выполнения действий условного оператора: оператор вызова этого класса должен получать три параметра. Он должен проверить свой первый параметр и, если эта проверка успешна, возвратить свой второй параметр; в противном случае он должен возвратить свой третий параметр.
Упражнение 14.35. Напишите класс, подобный классу PrintString, который читает строку из потока istream и возвращает строку, представляющую прочитанное. При неудаче чтения следует возвратить пустую строку.
Упражнение 14.36. Используя класс из предыдущего упражнения, организуйте чтение со стандартного устройства ввода, сохраняя каждую строку в векторе как элемент.
Упражнение 14.37. Напишите класс, проверяющий равенство двух значений. Используйте этот объект и библиотечные алгоритмы для написания кода замены всех экземпляров заданного значения в последовательности.