Lambda Expression VS Functor in C++

Lambda Expression vs Functor in C++

1) It's trivial and trying to share it is more work than benefit.

2) Defining a functor simply adds complexity (due to having to make a bunch of member variables and crap).

If neither of those things is true then maybe you should think about defining a functor.

Edit: it seems to be that you need an example of when it would be nice to use a lambda over a functor. Here you go:

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

Without lambdas you'd have to use something that similarly constructs a functor on the spot or write an externally linkable functor object for something that's annoyingly trivial.

BTW, I think maybe wtf_error is an extension.

When to use functors over lambdas

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.

Function pointer, Functor or Lambda?

Lambdas and functors are one of the most advanced C++ topics. You're better off starting with some fundamentals before, and have a solid understanding of how C++ classes work.

But, since you asked, the C++ equivalent of this should be something like this:

class Slider
{
public:
void onMouseDown()
{
if(rightClick or ctlKeyDown)
{
if(myFunctionPointer)
{
// show popup menu
myFunctionPointer(this);
}
}
}

std::function<void (Slider *)> myFunctionPointer=nullptr;
};

class Editor
{
public:
void showPopupMenu(Slider *s)
{
// build the popupMenu with information based on the slider
}

void init()
{
// create a slider and connect the popupMenu function to it
mySlider = new Slider;

mySlider->functionPointer = [this](Slider *)
{
showPopupMenu();
};

}

Slider *mySlider;
};

As I said, I think that you're better off focusing your efforts on getting the fundamentals down pat, first, before plunging into these shark-infested waters.

And just to add some additional color: this will compile (the only thing that's missing is the definitions of rightClick or ctlKeyDown), but it may or may not be right, depending on the scope and the lifetime of the objects involved. It may or may not be necessary to have the lambda capture a std::shared_ptr, instead of this, depending on how the objects in this application get instantiated. Understanding how C++ objects work would be a necessary prerequisite before dealing with closures and callbacks, of this sort.

Using functors with STL algorithms versus lambda expressions?

Yes you're right.

A lambda expression basically creates a nameless functor. It just allows the programmer to do it in less code. Before C++11 the standard algorithms could only work with functors which would require the programmer to set up a whole new class just for some specific behaviour (like you just did).

This is exactly the reason why lambda's were introduced into C++11, to make sure using the standard algorithms with a custom functor weren't such a pain to write anymore.

Speed of bound lambda (via std::function) vs operator() of functor struct

The difference between the two cases is fundamentally that with the functor, the compiler knows exactly what will be called at compile time, so the function call can be inlined. Lambdas, interestingly enough, also have a unique type. This means again, when you use a lambda, at compile type (since the compiler must know all types) the function being called is already known, so inlining can occur. On the other hand, a function pointer is type based only on its signature. The signature must be known so that it can be called to and returned from appropriately, but other than that a function pointer can point to anything at run-time, as far as the compiler is concerned. The same is true about std::function.

When you wrap the lambda in a std::function, you erase the type of the lambda from a compiler perspective. If this sounds weird/impossible, think of it this way: since a std::function of a fixed type can wrap any callable with the same signature, the compiler has no way of knowing that some other instruction won't come alone and change what the std::function is wrapping.

This link, http://goo.gl/60QFjH, shows what I mean (by the way, the godbolt page is very very handy, I suggest getting acquainted with it). I wrote three examples here similar to yours. The first uses std::function wrapping a lambda, the second a functor, the third a naked lambda (unwrapped), using decltype. You can look at the assembly on the right and see that both of the latter two get inlined, but not the first.

My guess is that you can use lambdas to do exactly the same thing. Instead of bind, you can just do value based capture with the lambdas of a and b. Each time you push back the lambda into the vector, modify a and b appropriately, and voila.

Stylistically though, I actually strongly feel you should use the struct. It's much clearer what's going on. The mere fact that you are seeming to want to capture a and b in one place, and test against c in another, means that this is used in your code in not just one place. In exchange for like, 2 extra lines of code, you get something more readable, easier to debug, and more extensible.

Difference between C++0x lambdas and operator(), closure and functor

Lambdas are really just syntactic sugar for a functor. You could do it all yourself: defining a new class, making member variables to hold the captured values and references, hooking them up in the constructor, writing operator()(), and finally creating an instance and passing it. Or you could use a lambda that's 1/10 as much code and works the same.

Lambdas which don't capture can be converted to function pointers. All lambdas can be converted to std::function, or get their own unique type which works well in templated algorithms accepting a functor.

Confusion about C++ functor/lambda argument-passing in STL algorithms

Your question is quite broad, but here is my concise answer.

1) In general, the guidelines for passing by-value vs by-reference in lambdas or functors are the same as they are for any regular function or method (a lambda is a functor created on the fly for you, which is a an object with an operator()(T)). The choice is mostly specific to your case, for example if the lambda/functor needs read-only access to its arguments you tipycally would pass a const reference.

2) Inside an algorithm that accepts a callable object as an argument (and as a template parameter) the compiler is bound to respect the rules of the language. Therefore parameters will be passed by value or by reference internally as per the signature of the lambda/functor.

Keep in mind that copy elision may enter into play, but that is a separate issue, not directly related to the fact that you are calling a lambda inside an standard library algorithm.

The example with int is too simple. I suggest you to experiment with actual objects.

3) C++ Standard provides precise definitions for the conditions where copy elision occurs, as well as the requirements on the signature of a lambda/functor parameter for a particular Standard Library algorithm.

However, it will not be easy in general to know if the internal implementation of a particular algorithm is going to call the lambda/functor in a way that meets copy elision conditions.

Note that the requirements on signature have some degree of flexibility, for example in the std::accumulate documentation we have

The signature of the function should be equivalent to the following:
Ret fun(const Type1 &a, const Type2 &b); The signature does not need
to have const &.

so you can choose to pass by value or by reference as you see fit.



Related Topics



Leave a reply



Submit