Double operator() ( doublex, doubley ) const

// ^ Аргументы как аргументы оригинальной лямбды

{

return3.0 * x / ( y + 2.0 ); // <- Тело как в теле оригинальной лямбды

}

};

 

std::transform(

vX.begin(), vX.end(), vY.begin(),

std::back_inserter( vR ),

Labmda_1()

);

 

Итак, ЛЯМБДА-выражение в языке С++ - это неименованный функциональный объект, тело и код создания которого автоматически генерируется компилятором на основе заданных программистом инструкций. Лямбда-выражения с подходящими форматами аргументов и возвращаемым типом могут быть использованы во всех конструкциях, где могут использоваться вызываемые сущности.

 

Основное преимущество лямбда-выражений по сравнению с комбинацией связывателей и элементарных функторов состоит в простоте написания и восприятия человеком. С точки зрения генерируемого машинного кода оба способа одинаково эффективны. По сравнению с создаваемыми для однократного использования в том или ином алгоритме обычными функциями или пользовательскими функциональными объектами, лямбда-выражения намного проще - не требуется отдельно что-либо декларировать и создавать, развертывание происходит автоматически во время сборки программы.

 

Эту же идею можно применить для упрощения ранее созданной вспомогательной функции для вывода содержимого последовательности на экран - предоставляемое лямбда-выражение максимально простым способом задает что именно нужно сделать с каждым из элементов перебираемой последовательности:

 

template< typenameInputIt >

voidprintCollection ( InputIt _first, InputIt _last, const char* _prefix )

{

std::cout << _prefix;

std::for_each( _first, _last,
[] ( typenameInputIt::value_type x )
{
std::cout << x << ' ';
}
);

std::cout << std::endl;

}

 

Еще одним примером может служить радикальное упрощение размера и читабельности кода, задающего компаратор для сортировки сотрудников, комбинирующий фамилию и возраст. Перепишем этот код при помощи лямбда-выражений:

 

std::sort(

employees.begin(), employees.end(),

[] ( constEmployee * _pEmployee1, constEmployee * _pEmployee2 ) -> bool

{

if( _pEmployee1->getName() < _pEmployee2->getName() )

return true;

 

else if( _pEmployee1->getName() == _pEmployee2->getName() )

return_pEmployee1->getAge() < _pEmployee2->getAge();

 

Else

return false;

}

);

 

Очевидно, применение лямбд намного привлекательнее прежних устаревающих способов записи и особенно комбинирования функциональных объектов. В целом, лямбда-выражения значительно разгружают сложность использования функциональных объектов для управления поведением алгоритмов. Это делает более привлекательным также и использование стандартных алгоритмов STL, ранее скованное сложностью записи каких-либо нетривиальных предикатов для реально встречающихся на практике случаев.

 

При необходимости, лямбда-выражения могут быть вложенными, однако не стоит увлекаться написанием настолько сложного для восприятия кода. Обычно, крупное лямбда-выражение стоит разбить на несколько вспомогательных функций.

 

Std::function

 

Пусть в программе имеется три варианта сортировки данных о сотрудниках:

● в алфавитном порядке по фамилии;

● в порядке убывания зарплаты;

● в порядке убывания возраста.

 

Предположим, выбор способа сортировки зависит от какой-либо внешней настройки, например, от аргумента командной строки или свойства в конфигурационном файле. Для упрощения, возьмем, что способ сортировки передается в подпрограмму в виде enum-аргумента:

 

enum classEmployeeSortMode

{

Alphabetical,

Salaries,

Age

};

 

Сортировку поместим во вспомогательную функцию, принимающую ссылку на вектор сотрудников, а также выбранный режим сортировки. Простое решение состоит в использовании блока switch для выполнения различных способов сортировки в зависимости от выбранного режима:

 

voidsortEmployees (

std::vector< Employee * > & _employees,

EmployeeSortMode _sortMode

)

{

// Выбираем способ сортировки в зависимости от режима

switch( _sortMode )

{

// Сортировка по алфавиту

caseEmployeeSortMode::Alphabetical:

std::sort(

_employees.begin(), _employees.end(),

[] ( constEmployee * _pEmployee1,
constEmployee * _pEmployee2 )

{

return_pEmployee1->getName() < _pEmployee2->getName();

}

);

break;

 

// Сортировка по возрасту

caseEmployeeSortMode::Age:

std::sort(

_employees.begin(), _employees.end(),

[] ( constEmployee * _pEmployee1,
constEmployee * _pEmployee2 )

{

return_pEmployee1->getAge() > _pEmployee2->getAge();

}

);

break;

 

// Сортировка по зарплате

caseEmployeeSortMode::Salaries:

std::sort(

_employees.begin(), _employees.end(),

[] ( constEmployee * _pEmployee1,
constEmployee * _pEmployee2 )

{

return _pEmployee1->getSalary() > _pEmployee2->getSalary();

}

);

break;

 

// Неизвестный режим сортировки

default:

throwstd::logic_error( "Unexpected sort mode" );

}

}

 

Такой вариант решения содержит немало повторяющегося кода по вызову алгоритма sort. По сути, между режимами сортировки отличается только лямбда-выражение, определяющее критерий сравнения сотрудников. Для упрощения кода имеет смысл выделить выбор предиката в зависимости от режима в отдельную функцию, а в данной функции вызывать сортировку лишь однократно.

 

Это намерение моментально наталкивается на вопрос - а какой тип имеют эти лямбда-выражения? До лямбда-выражений мы бы выбирали бы функцию сравнения при помощи указателя на функцию определенной сигнатуры, однако каждый функциональный объект и, соответственно, каждое лямбда-выражение, имеют свой уникальный тип. В случае лямбда-выражений этот тип еще и полностью зависит от компилятора. В контексте шаблонов это никого не смущает, поскольку тип лямбда-выражения нигде в коде явно не указывается. В нашей же ситуации необходимо записать какой-то конкретный тип для возврата лямбда-выражения из вспомогательной функции, который подойдет к каждому из 3 лямбда-выражений.

 

В стандартной библиотеке имеется специальное средство std::function, представляющее собой универсальную абстракцию вызываемой сущности определенного формата. Объявление для такого объекта выглядит следующим довольно необычным образом (все же, немного симпатичней, чем указатели на функции):

 

std::function< bool( constEmployee *, constEmployee * ) > comparator;

^ ^

возвращаемый -------- формальные аргументы -----
тип

 

Объект std::function можно инициализировать одним из возможных вариантов вызываемой сущности:

 

● указателем на функцию:

 

boolMyEmployeeCompareFunction ( constEmployee *, constEmployee * );

comparator = & myEmployeeCompareFunction;

 

● функциональным объектом:


structMyEmployeeCompareFunctor

{

bool operator() ( constEmployee *, constEmployee * ) const;

};

 

comparator = & MyEmployeeCompareFunctor();

 

● лямбда-выражением:

 

comparator = [] ( constEmployee *, constEmployee * ) { … }

 

После инициализации объект std::function можно свободно передавать по значению или по ссылке. Окружающий std::function код вовсе не обязан быть шаблонным. Для вызова используется стандартный функциональный синтаксис:

 

boolresult = comparator( employees[ 0 ], employees[ 1 ] );

 

Итак, преобразуем программу к желаемому виду. Пусть выбор лямбда-выражения в зависимости от желаемого режима сортировки происходит в следующей функции:

 

std::function< bool( constEmployee *, constEmployee * ) >

getEmployeeSortingPredicate ( EmployeeSortMode _sortMode )

{

// Выбираем предикат для сортировки в зависимости от режима

switch( _sortMode )

{

// Сортировка по алфавиту

caseEmployeeSortMode::Alphabetical:

return[] ( constEmployee * _pEmployee1, constEmployee * _pEmployee2 )

{

return_pEmployee1->getName() < _pEmployee2->getName();

};

 

// Сортировка по возрасту

caseEmployeeSortMode::Age:

return[] ( constEmployee * _pEmployee1, constEmployee * _pEmployee2 )

{

return_pEmployee1->getAge() > _pEmployee2->getAge();

};

 

// Сортировка по зарплате

caseEmployeeSortMode::Salaries:

return[] ( constEmployee * _pEmployee1, constEmployee * _pEmployee2 )

{

return_pEmployee1->getSalary() > _pEmployee2->getSalary();

};

 

// Неизвестный режим сортировки

default:

throwstd::logic_error( "Unexpected sort mode" );

}

}

 

Основная функция сортировки заметно упрощается:

 

voidsortEmployees ( std::vector< Employee * > & _employees,
EmployeeSortMode _sortMode )

{

std::sort(

_employees.begin(),

_employees.end(),

getEmployeeSortingPredicate( _sortMode ) // выбор предиката по режиму

);

}

 

Протестируем решение при помощи простой тестовой программы:

 

intmain ()

{

// Набор тестовых данных о сотрудниках

std::vector< Employee * > employees;

employees.push_back( newEmployee( "Ivanov", 25, 1000.0 ) );

employees.push_back( newEmployee( "Ivanov", 45, 2000.0 ) );

employees.push_back( newEmployee( "Petrov", 50, 750.0 ) );

employees.push_back( newEmployee( "Sidorov", 34, 1200.0 ) );

 

// Сортировка по возрасту

std::cout << "===== By Age ===== " << std::endl;

sortEmployees( employees, EmployeeSortMode::Age );

printEmployees( employees );

 

// Сортировка по зарплате

std::cout << "===== By Salary ===== " << std::endl;

sortEmployees( employees, EmployeeSortMode::Salaries );

printEmployees( employees );

 

// Сортировка по алфавиту

std::cout << "===== By Name ===== " << std::endl;

sortEmployees( employees, EmployeeSortMode::Alphabetical );

printEmployees( employees );

 

// Освобождение объектов

std::for_each(
employees.begin(), employees.end(),
[] ( Employee * pE ) { deletepE; }
);

}

 

В результате запуска получим следующий результат: