Интересно, где мы должны использовать лямбда-выражение над функтором в С++. Для меня эти два метода в основном одинаковы, даже функтор более изящный и чище, чем лямбда. Например, если я хочу повторно использовать свой предикат, мне придется копировать лямбда-часть снова и снова. Итак, когда на самом деле появляется лямбда?
Выражение Lambda vs. Functor в С++
Ответ 1
1) Это тривиально и пытается поделиться им - это больше работы, чем пользы.
2) Определение функтора просто добавляет сложности (из-за того, что нужно сделать кучу переменных-членов и дерьмо).
Если ни одна из этих вещей не верна, возможно, вам стоит подумать об определении функтора.
Изменить: похоже, вам нужен пример того, когда было бы удобно использовать лямбду над функтором. Вот вы:
typedef std::vector< std::pair<int,std::string> > whatsit_t;
int find_it(std::string value, whatsit_t const& stuff)
{
auto fit = std::find_if(stuff.begin(), stuff.end(), [value](whatsit_t::value_type const& vt) -> bool { return vt.second == value; });
if (fit == stuff.end()) throw std::wtf_error();
return fit->first;
}
Без lambdas вам нужно будет использовать что-то, что аналогично построит функтор на месте или напишет объект, связанный с внешней связью, для чего-то, что раздражающе тривиально.
Кстати, я думаю, возможно, wtf_error является расширением.
Ответ 2
Лямбда-выражение создает неназванный функтор, это синтаксический сахар.
Поэтому вы в основном используете его, если он делает ваш код лучше. Это обычно происходит, если либо (а) вы не собираетесь повторно использовать функтор, либо (б) вы собираетесь его повторно использовать, а из кода, полностью не связанного с текущим кодом, который, чтобы поделиться им, в основном в конечном итоге создайте my_favourite_two_line_functors.h
, и на нем будут разрозненные файлы.
Довольно же те же условия, при которых вы вводили бы любую строку (строки) кода, а не абстрагировали бы этот блок кода в функцию.
Тем не менее, с операторами range-for в С++ 0x есть некоторые места, где вы бы использовали функтор раньше, чем это могло бы сделать ваш код лучше выглядеть теперь, чтобы написать код как тело цикла, а не функтора или лямбда.
Ответ 3
Lambdas - это просто синтаксический сахар, который реализует функторы (NB: закрытие не просто.) В С++ 0x вы можете использовать ключевое слово auto для локального хранения lambdas, а функция std:: позволит вам хранить lambdas, или передавать их безопасным образом.
Ознакомьтесь с статьей Википедии о С++ 0x.
Ответ 4
Маленькие функции, которые не повторяются.
Основная жалоба на функторов заключается в том, что они не находятся в том же месте, где они использовались. Таким образом, вам нужно было найти и прочитать функтор вне контекста в том месте, где он использовался (даже если он используется только в одном месте).
Другая проблема заключалась в том, что функтор потребовал некоторую проводку для получения параметров в объекте-функторе. Не сложный, а весь базовый шаблонный код. И плита котла подвержена резанию и вставке.
Лямбда попытается исправить оба этих вопроса. Но я бы использовал функторы, если функция повторяется в нескольких местах или больше (не может придумать подходящий термин, поскольку она будет чувствительна к контексту).
Ответ 5
Лямбда и функтор имеют контекст. Функтор является классом и поэтому может быть более сложным, чем лямбда. Функция не имеет контекста.
#include <iostream>
#include <list>
#include <vector>
using namespace std;
//Functions have no context, mod is always 3
bool myFunc(int n) { return n % 3 == 0; }
//Functors have context, e.g. _v
//Functors can be more complex, e.g. additional addNum(...) method
class FunctorV
{
public:
FunctorV(int num ) : _v{num} {}
void addNum(int num) { _v.push_back(num); }
bool operator() (int num)
{
for(int i : _v) {
if( num % i == 0)
return true;
}
return false;
}
private:
vector<int> _v;
};
void print(string prefix,list<int>& l)
{
cout << prefix << "l={ ";
for(int i : l)
cout << i << " ";
cout << "}" << endl;
}
int main()
{
list<int> l={1,2,3,4,5,6,7,8,9};
print("initial for each test: ",l);
cout << endl;
//function, so no context.
l.remove_if(myFunc);
print("function mod 3: ",l);
cout << endl;
//nameless lambda, context is x
l={1,2,3,4,5,6,7,8,9};
int x = 3;
l.remove_if([x](int n){ return n % x == 0; });
print("lambda mod x=3: ",l);
x = 4;
l.remove_if([x](int n){ return n % x == 0; });
print("lambda mod x=4: ",l);
cout << endl;
//functor has context and can be more complex
l={1,2,3,4,5,6,7,8,9};
FunctorV myFunctor(3);
myFunctor.addNum(4);
l.remove_if(myFunctor);
print("functor mod v={3,4}: ",l);
return 0;
}
Выход:
initial for each test: l={ 1 2 3 4 5 6 7 8 9 }
function mod 3: l={ 1 2 4 5 7 8 }
lambda mod x=3: l={ 1 2 4 5 7 8 }
lambda mod x=4: l={ 1 2 5 7 }
functor mod v={3,4}: l={ 1 2 5 7 }
Ответ 6
Как вы указали, он работает лучше всего, когда вам нужно одноразовое, а накладные расходы на кодирование для его написания как функции не стоит.
Ответ 7
Концептуально, решение о том, что использовать, определяется тем же критерием, что и именованная переменная в сравнении с выражением на месте или константой...
size_t length = strlen(x) + sizeof(y) + z++ + strlen('\0');
...
allocate(length);
std::cout << length;
... здесь, создавая переменную длины, поощряет программу считать ее правильностью и значением в изоляции от нее позже. Надеемся, что имя надежно передает достаточно, чтобы его можно было понять интуитивно и независимо от его начального значения. Затем он позволяет использовать значение несколько раз без повторения выражения (при обращении z
к другому). Хотя здесь...
allocate(strlen(x) + sizeof(y) + z++ + strlen('\0'));
... общий код уменьшается, а значение локализуется в нужной точке. Единственное, что нужно "переносить вперед" из чтения этой строки, это побочные эффекты распределения и приращения (z), но нет дополнительной локальной переменной с объемом или более поздним использованием. Программист должен мысленно манипулировать меньшим состоянием, продолжая анализ кода.
То же самое относится к функциям по сравнению с встроенными операторами. Чтобы ответить на ваш вопрос, функторы против лямбда могут рассматриваться как частный случай этой функции против принятия решения.
Ответ 8
Во-первых, я хотел бы прояснить некоторые проблемы здесь.
Есть две разные вещи
- Лямбда-функция
- Лямбда-выражение/функтор.
Обычно лямбда-выражение, т.е. [] () {} -> return-type
, не всегда синтезируется в замыкание (т.е. вид функтора). Хотя это зависит от компилятора. Но вы можете заставить компилятор принудительно установить знак +
перед []
как +[] () {} -> return-type
. Это создаст указатель на функцию.
Теперь перейдем к вашему вопросу. Вы можете использовать лямбда несколько раз следующим образом:
int main()
{
auto print = [i=0] () mutable {return i++;};
cout<<print()<<endl;
cout<<print()<<endl;
cout<<print()<<endl;
// Call as many time as you want
return 0;
}
Вы должны использовать Lambda везде, где вам кажется, учитывая выразительность кода и простота обслуживания, как вы можете использовать его в пользовательских средствах удаления для интеллектуальных указателей & с большинством алгоритмов STL.
Если вы комбинируете Lambda с другими функциями, такими как constexpr
, variadic template parameter pack
или generic lambda
. Вы можете достичь многих вещей.
Вы можете найти больше об этом здесь