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(); }
};
У этого класса есть один член, являющийся оператором вызова функции, получающим две строки и сравнивающий их длины. Список параметров и тело функции те же, что и у лямбда-выражения. Как уже упоминалось в разделе 10.3.3, по умолчанию лямбда-выражения не могут изменять свои захваченные переменные. В результате по умолчанию оператор вызова функции в классе, созданном из лямбда-выражения, является константной функцией-членом. Если лямбда-выражение объявляется как mutable, то оператор вызова не будет константным.
Вызов функции stable_sort() можно переписать так, чтобы использовать этот класс вместо лямбда-выражения:
stable_sort(words.begin(), words.end(), ShorterString());
Третий аргумент — недавно созданный составной объект класса ShorterString. Код в функции stable_sort() будет вызывать этот объект каждый раз, когда он сравнивает две строки. При вызове объекта будет выполнено тело его оператора вызова, возвращающего значение true, если размер первой строки будет меньше, чем второй.
Классы, представляющие лямбда-выражения с захваченными переменными
Как уже упоминалось, при захвате лямбда-выражением переменной по ссылке разработчик должен сам гарантировать существование переменной, на которую ссылается ссылка, во время выполнения лямбда-выражения (см. раздел 10.3.3). Поэтому компилятору разрешено использовать ссылку непосредственно, не сохраняя ее как переменную-член в созданном классе.
Переменные, которые захватываются по значению, напротив, копируются в лямбда-выражение (см. раздел 10.3.3). В результате классы, созданные из лямбда-выражений, переменные которых захватываются по значению, имеют переменные-члены, соответствующие каждой такой переменной. У этих классов есть также конструктор для инициализации этих переменных-членов значениями захваченных переменных. В примере раздела 10.3.2 лямбда-выражение использовалось для поиска первой строки, длина которой была больше или равна заданному значению:
// получить итератор на первый элемент, размер которого >= sz
auto wc = find_if(words.begin(), words.end(),
[sz](const string &a)
Созданный класс выглядел бы примерно так:
class SizeComp {
SizeComp(size_t n) : sz(n) {} // параметр для каждой захваченной
// переменной
// оператор вызова с тем же типом возвращаемого значения, параметрами
// и телом, как у лямбда-выражения
bool operator()(const string &s) const
{ return s.size() >= sz; }
private:
size_t sz; // переменная-член для каждой переменной, захваченной
// по значению
};
В отличие от класса ShorterString, у этого класса есть переменная-член и конструктор для ее инициализации. У этого синтезируемого класса нет стандартного конструктора; чтобы использовать этот класс, следует передать аргумент:
// получить итератор на первый элемент, размер которого >= sz
auto wc = find_if(words.begin(), words.end(), SizeComp(sz));
У классов, созданных из лямбда-выражения, есть удаленный стандартный конструктор, удаленные операторы присвоения и стандартный деструктор. Будет ли у класса стандартный или удаленный конструктор копий/перемещения, зависит обычно от способа и типа захватываемых переменных-членов (см. раздел 13.1.6 и раздел 13.6.2).
Упражнения раздела 14.8.1
Упражнение 14.38. Напишите класс, проверяющий соответствие длины заданной строки указанному значению. Используйте такой объект в программе для оповещения о количестве слов во входном файле, имеющих размеры от 1 до 10 включительно.
Упражнение 14.39. Перепишите предыдущую программу так, чтобы сообщать количество слов размером от 1 до 9 и 10 или более.
Упражнение 14.40. Перепишите функцию biggies() из раздела 10.3.2 так, чтобы использовать объект функции вместо лямбда-выражения.
Упражнение 14.41. Как по-вашему, существенно ли добавление лямбда-выражений по новому стандарту? Объясните, когда имеет смысл использовать лямбда-выражение, а когда класс вместо него.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОК