29

Is there ever a situation where it makes more sense to create a functor than to use a lambda?

I know my question is effectively the reverse of when to use a lambda over a functor, but I can't think of a situation in practice where a functor would be preferred over a lambda. Any thoughts on this?

Community
  • 1
  • 1
sircodesalot
  • 11,231
  • 8
  • 50
  • 83
  • 1
    As an example, `std::function` is basically an advanced functor. Functors are particularly useful over lambdas when they're more complex. – Cornstalks Dec 02 '14 at 18:08
  • 1
    If you want to re-use it in a bunch of places. Usually a lambda's appeal (at least for me) is that it's in place. But if you need to use the same comparator (for example) in many places, a functor makes more sense. – Borgleader Dec 02 '14 at 18:08
  • 1
    Some people sadly still can't use C++11: I'd say that's one such situation. Not a good one though. – ereOn Dec 02 '14 at 18:08
  • If, for whatever reason you have a not very short comparator or predicate which has a state and you use in multiple locations (with different state, so imagine multiple instances of a functor, each used multiple times). It seems contrived, but it's imaginable. Additionally, if you need it to be a template and you don't have C++14 at hand. – PeterT Dec 02 '14 at 18:12
  • 7
    Debugging into a functor (is) may be nicer than debugging into a lambda –  Dec 02 '14 at 18:14

4 Answers4

43

A lambda is a functor - just defined with a shorter syntax.

The problem is that this syntax is limited. It doesn't always allow you to solve a problem in the most efficient and flexible way - or at all. Until C++14, the operator() couldn't even be a template.

Furthermore, a lambda has exactly one operator(). You can't provide several overloads to distinguish between, say, the types of the arguments:

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

.. or value category of the object argument (that is, the closure object that is called). You can also not define special member functions, including constructors and destructors - what if a functor shall be responsible for resources?

Another problem with lambdas is that they cannot be recursive. Of course, normal functions (including operator functions) can be.

Also consider that lambdas are unhandy to use as comparators for associative containers or deleters for smart pointers: You can't directly pass the closure type as a template argument and need to construct the containers member from another closure object. (Closure types don't have a default constructor!). For a block-scope map that isn't too much of a hassle:

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

Now, what happens if your map is a data member? What template argument, what initializer in the constructors initialization list? You'd have to use another static data member - but since you have to define it outside the classes definition that is arguably ugly.

To sum up: Lambdas aren't useful for more complex scenarios because they weren't made for them. They provide a short and concise way of creating simple function objects for correspondingly simple situations.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • 1
    inc(vote): Nor can they have `*this` is a `&&` overloads! And who doesn't want rvalue overloads on their function objects? – Yakk - Adam Nevraumont Dec 02 '14 at 18:13
  • 2
    "lambdas (...) cannot be recursive" That's not entirely true, you can make a lambda recursive, it's just that its type needs to be known in advance (e.g. by storing the lambda in `std::function`) and taking a reference to itself in the capture list. Practical? Not really, not to mention you cannot return a lambda like this (referencing a local), but technically possible. – Jiří Pospíšil Dec 02 '14 at 18:29
  • @JiříPospíšil I left that out for simplicity, and because it is impractical. – Columbo Dec 02 '14 at 18:31
  • No overloading ? [Template magic to the rescue](https://gist.github.com/dabrahams/3779345) ! – Quentin Dec 02 '14 at 20:03
  • 1
    @JiříPospíšil Do you feel like your lambda is non-recursive? Do you lack stack state and self reference? Apply a [Y combinator](http://coliru.stacked-crooked.com/a/4ba95aac15acde25) directly to the forehead. (Note this recursive lambda can be returned, and does not use type erasure) – Yakk - Adam Nevraumont Dec 02 '14 at 20:06
  • Lambdas allow the creation of local templates (polymorphic lambda). Local classes may not be templates and may not contain template members. – dyp Dec 02 '14 at 23:35
  • You cannot locally declare a function object type equivalent to a polymorphic lambda. (That's just another situation where you cannot use both.) Context: Either the OP (as part of an answer) or your answer (adding some additional info). – dyp Dec 03 '14 at 01:10
  • @dyp Yes, certainly, but this answer lists where you want functors over lambdas..? Or does that still fit? – Columbo Dec 03 '14 at 08:06
  • Good point. I think it is part of the consideration "functor or lambda?", i.e. "When would you use a functor instead of a lambda? You need to be able to use them, and then decide which one is nicer/easier/.." -- you could also answer negatively: "When you don't need a local `operator()` template" ;) – dyp Dec 03 '14 at 11:51
15

I would consider using a functor over a lambda when I

  • want more than one instance.
  • have to do advanced resource handling.
  • need to refer to the functor as a type. For example, to pass it as a template parameter.
  • (related) can give a meaningful and generally useful name to the type (as opposed to the instance).
  • find that the logic can be written cleaner when split up into sub-functions. In a lambda, we have to write everything into a single function.
5gon12eder
  • 24,280
  • 5
  • 45
  • 92
7

I could think of two cases:

  1. When functor carries internal state, thus there is a non-trivial life time issue with the functor. It keeps "something" between the uses

  2. When you have to use same code all over the place, writing and keeping it as a functor in its own header might be a good idea from maintenance point-of-view

Severin Pappadeux
  • 18,636
  • 3
  • 38
  • 64
5

A lambda can't be used in an unevaluated context. A particularly contrived example is stolen from Shafik's answer:

  1. In many cases it just useless since each lambda has a unique type, the hypothetical example given:

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

    The type of the lambda in the declaration and the call are different(by definition) and therefore this can not work.

So even a lambda with identical syntax cannot be used in place of another. A functor will work in this case.

Community
  • 1
  • 1