- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
Механизм захвата лямбда-выражения хранит полученную информацию между моментом создания лямбда-выражение (т.е. когда выполняется код определения лямбда-выражения) и моментом собственно выполнения лямбда-выражения. Разработчику следует самостоятельно позаботиться о том, чтобы независимо от момента захвата информации она осталась достоверной на момент выполнения лямбда-выражения.
Захват обычной переменной (типа int, string и так далее, но не указателя) обычно достаточно прост. В данном случае следует позаботиться о наличии у переменной значения в момент ее захвата.
При захвате указателя, итератора или переменной по ссылке следует удостовериться, что связанный с ними объект все еще существует на момент выполнения лямбда-выражения. Кроме того, объект в этот момент гарантированно должен иметь значение. Код, выполняемый между моментом создания лямбда-выражения и моментом его выполнения, может изменить значение объекта, на который указывает (или ссылается) захваченная сущность. Во время захвата указателя (или ссылки) значение объекта, возможно, и было правильным, но ко времени выполнения лямбда-выражения оно могло измениться.
Как правило, сокращая объем захватываемых данных, потенциальных проблем с захватом можно избежать. Кроме того, по возможности избегайте захвата указателей и ссылок.
Неявный захватВместо предоставления явного списка переменных содержащей функции, которые предстоит использовать, можно позволить компилятору самостоятельно вывести используемые переменные из кода тела лямбда-выражения. Чтобы заставить компилятор самостоятельно вывести список захвата, в нем используется символ & или =. Символ & указывает, что предполагается захват по ссылке, а символ = — что значения захватываются по значению. Например, передаваемое функции find_if() лямбда-выражение можно переписать так:
// sz неявно захватывается по значению
wc = find_if(words.begin(), words.end(),
[=](const string &s)
{ return s.size () >= sz; });
Если одни переменные необходимо захватить по значению, а другие по ссылке, вполне можно совместить явный и неявный захваты:
void biggies(vector<string> &words,
vector<string>::size_type sz,
ostream &os = cout, char c = ' ') {
// другие действия, как прежде
// os неявно захватывается по ссылке;
// с явно захватывается по значению
for_each(words.begin(), words.end(),
[&, c](const string &s) { os << s << c; });
// os явно захватывается по ссылке;
// с неявно захватывается по значению
for_each(words.begin(), words.end(),
[=, &os](const string &s) { os << s << c; });
}
При совмещении неявного и явного захвата первым элементом в списке захвата должен быть символ & или =. Эти символы задают режим захвата по умолчанию: по ссылке или по значению соответственно.
При совмещении неявного и явного захвата явно захваченные переменные должны использовать дополнительную форму. Таким образом, при неявном захвате по ссылке (с использованием &) явно именованные переменные должны захватываться по значению; следовательно, их именам не может предшествовать символ &. И наоборот, при неявном захвате по значению (с использованием =) явно именованным переменным должен предшествовать символ &, означающий, что они должны быть захвачены по ссылке.
Изменяемые лямбда-выраженияПо умолчанию лямбда-выражение не может изменить значение переменной, которую она копирует по значению. Чтобы изменить значение захваченной переменной, за списком параметров должно следовать ключевое слово mutable. Изменяемые лямбда-выражения не могут пропускать список параметров:
void fcn3() {
size_t v1 = 42; // локальная переменная
// f может изменить значение захваченных переменных
auto f = [v1]() mutable { return ++v1; };
v1 = 0;
auto j = f(); // j = 43
}
Может ли захваченная по ссылке переменная быть изменена, зависит только от того, ссылается ли она на константный или неконстантный тип:
void fcn4() {
size_t v1 = 42; // локальная переменная
// v1 - ссылка на неконстантную переменную
// эту переменную можно изменить в f2 при помощи ссылки
auto f2 = [&v1] { return ++v1; };
v1 = 0;
auto j = f2(); // j = 1
}
Определение типа возвращаемого значения лямбда-выраженияИспользованные до сих пор лямбда-выражения содержали только один оператор return. В результате тип возвращаемого значения определять было не нужно. По умолчанию, если тело лямбда-выражения содержало какие-нибудь операторы, кроме оператора return, то подразумевалось, что оно возвращало тип void. Подобно другим функциям, возвращающим тип void, подобные лямбда-выражения могут не возвращать значения.
В качестве примера используем библиотечный алгоритм transform() и лямбда-выражение для замены каждого отрицательного значения в последовательности его абсолютным значением:
transform(vi.begin(), vi.end(), vi.begin(),
[](int i) { return i < 0 ? -i : i; });
Функция transform() получает три итератора и вызываемый объект. Первые два итератора обозначают исходную последовательность, третий итератор — назначение. Алгоритм вызывает переданный ему вызываемый объект для каждого элемента исходной последовательности и записывает результат по назначению. Как и в данном примере, итератор назначения может быть тем же, обозначающим начало ввода. Когда исходный итератор и итератор назначения совпадают, алгоритм transform() заменяет каждый элемент в исходном диапазоне результатом вызова вызываемого объекта для этого элемента.
В этом вызове передавалось лямбда-выражение, которое возвращает абсолютное значение своего параметра. Тело лямбда-выражения — один оператор return, который возвращает результат условного выражения. Необходимости определять тип возвращаемого значения нет, поскольку его можно вывести из типа условного оператора.
Но если написать на первый взгляд эквивалентную программу, используя оператор if, то код не будет компилироваться:
// ошибка: нельзя вывести тип возвращаемого значения лямбда-выражения
transform(vi.begin(), vi.end(), vi.begin(),
[](int i) {if (i < 0) return -i; else return i; });
Эта версия лямбда-выражения выводит тип возвращаемого значения как void, но возвращает значение.
Когда необходимо определить тип возвращаемого значения для лямбда-выражения, следует использовать замыкающий тип возвращаемого значения (см. раздел 6.3.3):
transform(vi.begin(), vi.end(), vi.begin(),
[](int i) -> int
{ if (i < 0) return -i; else return i; });
В данном случае четвертым аргументом функции transform() является лямбда-выражение с пустым списком захвата, единственным параметром типа int и возвращаемым значением типа int. Его телом является оператор if, возвращающий абсолютное значение параметра.
Упражнения раздела 10.3.3Упражнение 10.20. Библиотека определяет алгоритм count_if(). Подобно алгоритму find_if(), он получает пару итераторов, обозначающих исходный диапазон и предикат, применяемый к каждому элементу заданного диапазона. Функция count_if() возвращает количество раз, когда предикат вернул значение true. Используйте алгоритм count_if(), чтобы переписать ту часть программы, которая рассчитывала количество слов длиной больше 6.
Упражнение 10.21. Напишите лямбда-выражение, которое захватывает локальную переменную типа int и осуществляет декремент ее значения, пока оно не достигает 0. Как только значение переменной достигнет 0, декремент переменной прекращается. Лямбда-выражение должно возвратить логическое значение, указывающее, имеет ли захваченная переменная значение 0.
10.3.4. Привязка аргументов
Лямбда-выражения особенно полезны для простых операций, которые не предполагается использовать в более чем одном или двух местах. Если ту же операцию необходимо осуществлять во многих местах, то обычно определяют функцию, а не повторяют то же лямбда-выражение многократно. Аналогично, если операция требует многих операторов, обычно лучше использовать функцию.

