В качестве примера определим класс, выводящий строковый аргумент. По умолчанию класс будет писать в поток 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; //
printer(s); //
PrintString errors(cerr, '\n');
errors(s); //
Объекты функции обычно используют как аргументы для обобщенных алгоритмов. Например, для вывода содержимого контейнера можно использовать класс PrintString и библиотечный алгоритм for_each() (см. раздел 10.3.2):
for_each(vs.begin(), vs.end(), PrintString(cerr, '\n'));
Третий аргумент алгоритма for_each() является временным объектом типа PrintString, инициализируемый потоком cerr и символом новой строки. Вызов функции for_each() выводит каждый элемент vs в поток cerr, разделяя их новой строкой.
Упражнение 14.33. Сколько операндов может иметь перегруженный оператор вызова функции?
Упражнение 14.34. Определите класс объекта функции для выполнения действий условного оператора: оператор вызова этого класса должен получать три параметра. Он должен проверить свой первый параметр и, если эта проверка успешна, возвратить свой второй параметр; в противном случае он должен возвратить свой третий параметр.
Упражнение 14.35. Напишите класс, подобный классу PrintString, который читает строку из потока istream и возвращает строку, представляющую прочитанное. При неудаче чтения следует возвратить пустую строку.
Упражнение 14.36. Используя класс из предыдущего упражнения, организуйте чтение со стандартного устройства ввода, сохраняя каждую строку в векторе как элемент.
Упражнение 14.37. Напишите класс, проверяющий равенство двух значений. Используйте этот объект и библиотечные алгоритмы для написания кода замены всех экземпляров заданного значения в последовательности.
14.8.1. Лямбда-выражения — объекты функции
В предыдущем разделе объект PrintString использовался как аргумент в вызове функции for_each(). Это похоже на программу, написанную в разделе 10.3.2, где использовалось лямбда-выражение. Написанное лямбда-выражение компилятор преобразовывает в безымянный объект безымянного класса (см. раздел 10.3.3). Классы, созданные из лямбда-выражения, содержат перегруженный оператор вызова функции. Рассмотрим, например, лямбда-выражение, передававшееся как последний аргумент функции stable_sort():
//
//
stable_sort(words.begin(), words.end(),
[](const string &a, const string &b)
{ return a.size() < b.size();});
Это действует как безымянный объект класса, который выглядел бы примерно так:
class ShorterString {
public:
bool operator()(const string &s1, const string &s2) const
{ return s1.size() < s2.size(); }
};