Подтвердить что ты не робот

Когда использовать функторы над лямбдами

Есть ли когда-нибудь ситуация, когда имеет смысл создавать функтор, чем использовать лямбда?

Я знаю, что мой вопрос действительно обратный когда использовать лямбду над функтором, но я не могу представить ситуацию на практике, где функтор предпочтительнее лямбда. Любые мысли об этом?

4b9b3361

Ответ 1

Лямбда - это функтор, просто определяемый коротким синтаксисом.

Проблема заключается в том, что этот синтаксис ограничен. Это не всегда позволяет вам решать проблему наиболее эффективным и гибким способом - или вообще. До С++ 14, operator() не мог даже быть шаблоном.

Кроме того, лямбда имеет ровно один operator(). Вы не можете предоставить несколько перегрузок, чтобы различать, скажем, типы аргументов:

struct MyComparator
{
    bool operator()( int a, int b ) const {return a < b;}
    bool operator()( float a, float b ) const {return /*Some maths here*/;}
};

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

Другая проблема с лямбдами заключается в том, что они не могут быть рекурсивными. Конечно, нормальные функции (включая операторные функции) могут быть.

Также учтите, что lambdas не могут использовать в качестве компараторов для ассоциативных контейнеров или удалений для интеллектуальных указателей: вы не можете напрямую передать тип закрытия в качестве аргумента шаблона и должны построить элемент контейнера из другого объекта-закрытия. (Типы Closure не имеют конструктора по умолчанию!). Для блока-области map, который не слишком много хлопот:

auto l = [val] (int a, int b) {return val*a < b;};
std::map<int, int, decltype(l)> map(l);

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

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

Ответ 2

Я хотел бы использовать функтор над лямбдой, когда I

  • требуется более одного экземпляра.
  • необходимо выполнить расширенную обработку ресурсов.
  • нужно ссылаться на функтор как на тип. Например, передать его в качестве параметра шаблона.
  • (связанный) может дать осмысленное и обычно полезное имя для типа (в отличие от экземпляра).
  • найдите, что логика может быть написана более чистой, если она разделена на подфункции. В лямбда мы должны написать все в одну функцию.

Ответ 3

Я мог бы думать о двух случаях:

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

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

Ответ 4

Лямбда не может быть использована в неоценимом контексте. Особенно надуманный пример украден из ответа Шафика:

  1. Во многих случаях это просто бесполезно, поскольку каждая лямбда имеет уникальный тип, гипотетический пример:

    template<typename T, typename U>
    void g(T, U, decltype([](T x, T y) { return x + y; }) func);
    
    g(1, 2, [](int x, int y) { return x + y; });
    

    Тип лямбда в объявлении и вызове разные (по определению), и поэтому это не может работать.

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