Целью данного раздела является внедрение базового функционала библиотеки. Исключений и особых случаев довольно много, но обсуждение их откладывается до более поздних разделов.
Introductory Examples
В этом разделе мы приводим основные примеры использования выражений лямбда BLL в вызовах алгоритма STL. Мы начинаем с простых выражений и работаем. Во-первых, мы инициализируем элементы контейнера, скажем,<list>, к значению.<1>:
Выражение<&_1>создает объект функции для получения адреса каждого элемента в<v>. Адреса присваиваются соответствующим элементам в<vp>.
Следующий фрагмент кода изменяет значения в<v>. Для каждого элемента называется функция<foo>. Первоначальное значение элемента передается в качестве аргумента<foo>. Результат<foo>присваивается обратно элементу:
int foo(int);
for_each(v.begin(), v.end(), _1 = bind(foo, _1));
Следующим шагом является сортировка элементов<vp>:
sort(vp.begin(), vp.end(), *_1 > *_2);
В этом призыве к<sort>мы сортируем элементы по их содержимому в порядке убывания.
Наконец, следующий<for_each>вызов выводит сортированное содержимое<vp>, разделенное разрывами линий:
Обратите внимание, что нормальное (не-ламбда) выражение как субэкспрессия выражения лямбда оценивается немедленно. Это может вызвать сюрпризы. Например, если предыдущий пример переписан как
Субэкспрессия<cout << '\n'>оценивается немедленно, и эффект состоит в том, чтобы вывести один разрыв линии, за которым следуют элементы<vp>. BLL обеспечивает функции<constant>и<var>для превращения констант и, соответственно, переменных в лямбда-выражения и может использоваться для предотвращения немедленной оценки подвыражений:
Во время вызова лямбда-функтора фактические аргументы заменяются заполнителями. Заполнители не диктуют тип этих фактических аргументов. Основное правило заключается в том, что лямбда-функция может быть вызвана аргументами любых типов, если лямбда-выражение с выполненными заменами является действительным выражением C++. В качестве примера выражение<_1 + _2>создает двоичный лямбда-функтор. Его можно назвать с двумя объектами любых типов<A>и<B>, для которых определен<operator+(A,B)>(и для которых BLL знает тип возврата оператора, см. ниже).
C++ не имеет механизма для запроса типа выражения. Однако этот точный механизм имеет решающее значение для реализации лямбда-выражений C++. Следовательно, BLL включает в себя несколько сложную систему дедукции типа, которая использует набор классов признаков для дедукции результирующего типа функций лямбда. Он обрабатывает выражения, в которых операнды имеют встроенные типы, и многие выражения со стандартными типами библиотек. Многие из определяемых пользователем типов также охватываются, особенно если операторы, определяемые пользователем, соблюдают обычные соглашения при определении типов возврата.
Однако бывают случаи, когда тип возврата не может быть выведен. Предположим, что вы определили:
C operator+(A, B);
Вызов функции лямбда не выполняется, так как тип возврата не может быть выведен:
Для выражений связывания тип возврата может быть определен как шаблонный аргумент функции связывания:
bind<int>(foo, _1, _2);
About actual arguments to lambda functors
Общее ограничение для реальных аргументов состоит в том, что они не могут быть неконстными значениями. Например:
int i = 1; int j = 2;
(_1 + _2)(i, j); // ok
(_1 + _2)(1, 2); // error (!)
Это ограничение не так плохо, как может показаться. Поскольку лямбда-функторы чаще всего называются внутри STL-алгоритмов, аргументы исходят из итераторов дереференций, а операторы дереференций редко возвращают значения r. И в тех случаях, когда они это делают, есть обходные пути, обсуждаемые вразделе под названием & #8220; Значения как фактические аргументы для лямбда-функторов & #8221;.
Storing bound arguments in lambda functions
По умолчанию временные копии связанных аргументов хранятся в лямбда-функторе. Это означает, что значение связанного аргумента фиксируется в момент создания функции лямбда и остается постоянным в течение жизни объекта функции лямбда. Например:
int i = 1;
(_1 = 2, _1 + i)(i);
Оператор запятой перегружен, чтобы объединить выражения лямбда в последовательность; получившийся унарный лямбда-функтор сначала присваивает 2 своему аргументу, а затем добавляет к нему значение<i>. Значение выражения в последней строке 3, а не 4. Иными словами, лямбда-выражение, которое создается<lambda x.(x = 2, x + 1)>, а не<lambda x.(x = 2, x + i)>.
Как уже говорилось, это поведение по умолчанию, для которого есть исключения. Точные правила следующие:
Программист может управлять механизмом хранения с помощью<ref>и<cref>обертокref. Обертывая аргумент<ref>или<cref>, библиотека предписывает хранить аргумент в качестве ссылки или ссылки на const соответственно. Например, если мы перепишем предыдущий пример и завернем переменную<i>в<ref>, мы создадим лямбда-выражение<lambda x.(x = 2, x + i)>и значение выражения в последней строке будет 4:
<
i = 1;
(_1 = 2, _1 + ref(i))(i);
>.
Обратите внимание, что<ref>и<cref>отличаются от<var>и<constant>. В то время как последние создают лямбда-функторы, первые — нет. Например:
<
int i;
var(i) = 1; // ok
ref(i) = 1; // not ok, ref(i) is not a lambda functor
>
Функции<ref>и<cref>в основном существуют по историческим причинам, и<ref>всегда можно заменить на<var>и<cref>на<constant_ref>. См.раздел под названием “ Задержка констант и переменных”для деталей. Функции<ref>и<cref>являются функциями полезности общего назначения в Boost и, следовательно, определяются непосредственно в пространстве имен<boost>.
Типы массивов не могут быть скопированы, поэтому они хранятся в качестве ссылки по умолчанию.
Для некоторых выражений имеет смысл хранить аргументы в качестве ссылок. Например, очевидное намерение лямбда-выражения<i += _1>состоит в том, что призывы к лямбда-функтору влияют на значение переменной<i>, а не на некоторую временную ее копию. В качестве другого примера операторы потокового вещания используют свой самый левый аргумент в качестве неконстовых ссылок. Правила следующие:
Левый аргумент операторов присвоения соединений (<+=>,<*=>и т. д.) сохраняется в качестве ссылок на неконст.
Если левый аргумент оператора<<<>или<>>>получен из инстанциации<basic_ostream>или соответственно из<basic_istream>, аргумент хранится как ссылка на неконст. Для всех остальных типов аргумент хранится в виде копии.
В указательных арифметических выражениях неконстовые типы массивов сохраняются как неконстовые ссылки. Это делается для предотвращения арифметики указателей, делающих неконстовые массивы конст.
<&_1>Строго говоря, стандарт C++ определяет<for_each>какнемодифицирующую операцию последовательности, и объект функции, переданный<for_each>, не должен изменять свой аргумент. Требования к аргументам<for_each>необязательно строги, поскольку до тех пор, пока итераторыизменчивы,<for_each>принимает объект функции, который может иметь побочные эффекты на их аргументацию. Тем не менее, легко предоставить другой шаблон функций с функциональностью<std::for_each>, но более четкими требованиями к его аргументам.
Статья Using the library раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 18. Boost.Lambda может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.