Лямбда-выражение против Функтора в C ++ - PullRequest
36 голосов
/ 14 января 2011

Интересно, где мы должны использовать выражение лямбда над функтором в C ++. Для меня эти две техники в основном одинаковы, даже функтор более элегантен и чище, чем лямбда . Например, если я хочу повторно использовать свой предикат, я должен копировать лямбда-часть снова и снова. Так когда же лямбда действительно приходит на место?

Ответы [ 7 ]

17 голосов
/ 14 января 2011

Лямбда-выражение создает безымянный функтор, это синтаксический сахар.

Таким образом, вы в основном используете его, если он делает ваш код лучше. Обычно это происходит, если (а) вы не собираетесь повторно использовать функтор, или (б) вы собираетесь использовать его повторно, но из кода, совершенно не связанного с текущим кодом, что для его совместного использования вы в основном в итоге создаем my_favourite_two_line_functors.h, и от него зависят разные файлы.

Практически те же условия, при которых вы вводите любые строки кода, а не абстрагируете этот блок кода в функцию.

Тем не менее, с помощью операторов range-for в C ++ 0x, есть некоторые места, где вы раньше использовали бы функтор, где он мог бы сделать ваш код лучше теперь, чтобы писать код как тело цикла, а не функтор или лямбда.

15 голосов
/ 14 января 2011

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;
}

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

Кстати, я думаю, что wtf_error - это расширение.

11 голосов
/ 14 января 2011

Лямбды в основном просто синтаксический сахар, который реализует функторы (NB: замыкания не простые.) В C ++ 0x вы можете использовать ключевое слово auto для локального хранения лямбд, а std :: function позволяет хранить лямбды или передавать их в безопасном для типов виде.

Ознакомьтесь с статьей Википедии о C ++ 0x.

8 голосов
/ 14 января 2011

Небольшие функции, которые не повторяются.

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

Другая проблема заключалась в том, что функтору потребовалось некоторое подключениепараметры в объект функтора.Не сложный, но весь базовый шаблонный код.И плита котла подвержена проблемам резки и вставки.

Лямбда-попытка исправить и то и другое.Но я бы использовал функторы, если функция повторяется в нескольких местах или она больше (не может придумать подходящий термин, так как она будет контекстно-зависимой) small.

1 голос
/ 21 июля 2018

лямбда и функтор имеют контекст. Функтор является классом и поэтому может быть более сложным, чем лямбда. Функция не имеет контекста.

#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 }
0 голосов
/ 14 января 2011

Концептуально решение о том, какое использование использовать, определяется тем же критерием, что и использование именованной переменной в сравнении с выражением или константой на месте ...

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), но нет дополнительной локальной переменной с областью действия или более поздним использованием, которое следует учитывать.Программист должен мысленно манипулировать меньшим количеством состояний, продолжая анализ кода.

То же различие применяется к функциям и встроенным операторам.Чтобы ответить на ваш вопрос, функторы против лямбд можно рассматривать как частный случай этой функции, а не как встроенное решение.

0 голосов
/ 14 января 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...