C++0x lambdas suck

int x = 3 ;
int y = 2 ;
vector<int> v = { 1, 2, 3, 4, 5, 6 } ;

sort(v.begin(), v.end(), [=x, &y](int lhs, int rhs) { return (lhs & x) < (rhs & y); }) ;

Ok – I stretched there to incorporate the capture in all it’s glory (the capture is the [=x,&y]).

Lets be frank here. The purpose of a lambda function is to be able to create a little snippet of code to be executed inside a loop.

Does C++ need it? IMHO, lambda functions are bad. And C++0x’s choice to make them hideously ugly doubly so.

The ability to stuff multiple instructions into a single line is a topic of much debate. Maintenance programmers hate it – most maintenance programmers go by the rule of 1; that is, one operation per line.

Lambda functions are an encouragement to stuff not just multiple operations into one line but multiple statements into one operation.

sort(v.begin(), v.end(), [=x, &y](int lhs, int rhs) {
  lhs *= x ;
  rhs /= y ;

  return (x < y) ;
}) ;

C++ already has passable mechanisms for lambdas and captures in the form of function objects. And I think moving away from clearer, easier to maintain code is like choosing blue rinse to cover your gray hairs.

For very simple operations, it is nice to be able to put the operation inside the sort…

sort(v.begin(), v.end(), { return arg1 < arg2 ; });

Having to create a whole function body for that is a bit mindless.

There are two other motivations for people wanting formally defined lambdas and closures in C++: clutter and, well, clutter.

With current C++, creating function objects is a bit of a pain because they have to be done out-of-line. You can’t define a function object inside a function definition, it has to be done at a higher scope level, which makes for cluttered reading. And the syntax for doing it with structs and classes is currently convoluted (if you are doing a closure, you have to write a constructor, blah blah blah).

I would have preferred something like:


int myFn(vector<Object*> a, int index) {

  lambda int SortObjectsByPointer (Object* lhs, Object* rhs) {
    return lhs < rhs ;
  }

  sort(a.begin(), a.end(), SortObjectsByPointer) ;

  closure SortObjectsByIndex(int index) {
    int (Object* lhs, Object* rhs) {
      return lhs->data[index] < rhs->data[index] ;
    }
  }

  sort(a.begin(), a.end(), SortObjectsByIndex) ;
}

In the above closure, “int index” captures the current value of ‘index’, if I wanted to capture a reference to it, I’d write “int& index”.

The above given example, though, would expand itself out to the equivalent of:

class myFn::SortObjectsByIndex {
  int index ;
public:
  SortObjectsByIndex(int _index) : index(_index) {}
  int operator()(Object* lhs, Object* rhs) {
    return lhs->data[this->index] < rhs->data[this->index] ;
  }
}

sort(a.begin(), a.end(), SortObjectsByIndex(index)) ;

As far as I can tell, C++0x actually tackles several of the side issues that make function objects less desirable than proper lambda functions. But on the whole, what people want is recognition of this very common coding practice and a formal expression for it so that the code more readily self annotates. The expanded version above does its job, functionally, but it is full of irrelevant boilerplate.

C++0x lambdas/closures are going to be a pain in the derrière. For a start, which is which is distinguishable by whether you used [=] or [&] or a variant thereof…

19 Comments

If I remember right, there is a possibility to write it this way:

auto myLambda = [&amp;]
{
  // whatever
};

std::for_each(v.begin(), v.end(), myLambda);

A bit better, but still not very good, I agree. I also don’t like the syntax of the lambdas. Especially if you define the return value it looks very ugly.

In simple loops it’s more trouble/ugliness than it’s worth, but I think judging lambdas based on little tutorial examples is a bit premature.


at_scope([&] { x.push() ; }, [&] { x.pop() ; } ) {
...
}


void Person::start_pacing(void) {
bool moving_left = true ;
this->on_update =
[=] (void) mutable {
if ( moving_left ) this->move_left() ;
else this->move_right() ;
moving_left = ! moving_left ;
} ;
}

It would be nice to have details automated in circumstances like the parameter types with std::for_each, but they can make better code in the right circumstances.

Grr… I’ll get those code tags someday. Get a preview button already!

I think you misunderstand, Vict; its the inlining nature of Lambda functions that I dislike.

 document.eventListener = function (e) { print(e.name) ; } ;

I’m as likely as the next person to write something like that when hacking away in Javascript.

The variant Drave points out – well that I like.

The Javascript example demonstrates the ground where arguing Lambdas are a source of bad code is a stretch. The problem is there’s no upper limit on the complexity of a function that can be a lambda function, and so they’ll grow.

Getting good test coverage of Lambda functions is problematic, which again goes from a stretch to a hell mouth of bugs if the lambda function undergoes tweakage.

In C++0x I rate them from bad to suck because I find the syntax quite hideous and would really have liked to have seen a design that didn’t look like it came from a 1970s language design class recovering from a bad acid trip.

I definitely understand that. Most “Hey, look at this neat feature!” examples use inline lambdas, usually with std::for_each, but I’d honestly prefer a for loop with ‘auto it = …’, never mind the also-added range-based for-loop: It’s just too much syntax crammed into one place to be easily readable.

The way I’ve used them so far is basically to split complicated functions into smaller ones, especially where there’s a nice amount of repetition. It’s kind of like making a function into a class with state variables, just a hell of a lot easier to write and (believe it or not) read. Yeah, it’s definitely a syntax that matches the age of the language, but it’s not too bad if it’s in a place that justifies it. It’s like the syntax for declaring types, especially when the variable is declared with it: It’s verbose and full of ugly (struct { int x ; } foo ;), but it’s also completely unnecessary most of the time and generally not too bad when it is, so who cares?
I suppose testability could be an issue since (as I usually use them) the lambdas are inaccessible outside where they’re declared. I guess that really depends on what/how you’re testing though, and could probably be dealt with if/when it’s worth it.

Actually you are wrong about C++0x closure you can do this for example to count the number of values less than 5 in a vector. It the lambda functionality is pretty powerful.

#include
#include
#include

function maxCmp = [] (int a, int b)->bool{return a<b;};

std::count_if(vect.begin(),vect(end),boost::bind(maxCmp,_1,5));

Can you post that again, wp ate it … put {sourcecode language=’cpp’} … {/sourcecode} around it but replace {} with [].

– Oliver

Sorry, for replying so late. Hope this helps!

#include
#include
#include

function maxCmp = [] (int a, int b)->bool{return a<b;};

std::count_if(vect.begin(),vect(end),boost::bind(maxCmp,_1,5));

Wow! I put the and still it ate it :(. The included header filesfiles are boost/bind.hpp and functional, ans the algorithm headers, ofcourse you could throw in the vector one as well

Your post shows that you don’t know how to use lambdas properly. Do your homework before you say something “sucks”.

I’ve rewrote your examples using the C++11 lambda syntax and as you can see, the syntax is quite similar.

btw, why are you passing Object by pointer instead of reference and why are you returning an int instead of a bool?

lambda int SortObjectsByPointer (Object* lhs, Object* rhs) {
return lhs < rhs ;
}

auto SortObjectsByPointer = [] (Object* lhs, Object* rhs) {
return lhs data[index] data[index] ;
}
}

auto SortObjectsByIndex = [index] (Object* lhs, Object* rhs) {
return lhs->data[index] data[index] ;
}

Ricky:

Your comment shows that you don’t know how to read properly. Try reading the entire article rather than just one code snippet before you claim someone doesn’t know “how to use” something.

But lets answer your questions in the hopes that unburdening your mind might reduce the matter to one you are capable of comprehending.

auto SortObjectsByIndex = [index] (Object* lhs, Object* rhs) {
return lhs->data[index] < data[index] ;
}

At the time I wrote this post, in June 2010, none of the compilers available supported that use of auto syntax. Do your homework.

Secondly, that syntax is hideous. Not the auto part, which is just plain ugly, but then I posted elsewhere that I think “auto” is bad and that is just another example where it is going to get the heck used out of it causing confusion and headaches in what is supposed to be a strongly, statically typed language. No, my issue is with the weird “[…]” operator used for Lambdas.

Why am I passing Objects by pointer? Because “vector<Object*>”. Do your basic reading.

I like lambdas in other languages. For instance, it’s very convenient to write in Haskell:
list1 = map (\ x -> (x > 5) 0 1)

or in python:
list1 = map(lambda x : 0 if x > 5 else 1, list2)

But the correspondent in C++0x is hideous:

vect1 = transform(vect2.begin(), vect2.end(), [](auto x){return x>5?0:1;})

It’s no transparent at all.

For a video on the matter with some good use cases for lambda’s, I’d suggest a checking out a talk by Herb Sutter. I agree that the syntax takes some getting used to, but to me he made a lot of good reasons to use them.

http://herbsutter.com/2011/05/20/my-lambdas-talk-nwcpp-is-now-online/

Who says you can’t define a function object within a function definition?

void Test()
{
	array<int, 10> a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	struct pred { bool operator()(int a){ return (a % 2) == 0; } };
	count_if(a.begin(), a.end(), pred());
}

“The purpose of a lambda function is to be able to create a little snippet of code to be executed inside a loop” – who told you so? There are plenty of other (far more important) uses.

Watch Herb Sutter’s video posted by kalaxy then update your blog post. It makes the code a LOT cleaner. Takes a little getting used too, but we’re programmers. Adapt and improve.

kfsone, you my friend are an idiot. We’re constantly using lambdas in languages such as C# and enjoyed the benefits there, and Java has finally picked up the concept as well. I applaud the initiative to bring this wonderful world to C++, granted even if it has a slightly ugly notation. Soon the libraries will all use it. It’s a very convenient concept with a huge applicability, for example filtering, class substitutions, ordering, aggregations, callbacks and what not. Get your head out of the sand and see the world around you.

@MoonStorm I’ve actually explained and continued into the comments that I generally think lambdas are bad and the basis of that starting point. But that *is* a generalization. I use lambdas in JavaScript all the time, The point of this post is that the spec in C++0x sucked, and while they have made some significant improvements in ++14 (http://en.wikipedia.org/wiki/C++14#Generic_lambdas) the syntax is still awful and – based on my experience working with them the last year, they do a terrible job at one of the core purposes that got them into the language, loop functors, because they aren’t context aware, so the only control flow option available to you is return.

You can abrogate some of that by creating accessor functions to do the workload:

    find_if(begin(), end(), [&](const T& t) {
        return t.findIfMatch(a, b, c, d, ...);
    });

The other problem is that, although not as low-level as C, C++ is a pretty low-level language. Drawing comparisons with C# is highly misguided. “auto” and “lambdas” are potential performance bear traps if used incorrectly.

    auto myAccount = *it; // Whoops - you mean't auto&
    for (auto theirAccount : accounts) { // Whoops - you mean't auto&
        myAccount -= 1000; // modifying copy of account
        theirAccount += 1000; // modifying copy of account
    }

Never mind that you changed copies of objects which you then destroyed, you created copies of each object which was a massive waste of CPU cycles.

C++’s lambdas have very sharp edges. In trivial cases, they are highly performant, infact the compiler can do really smart things with them and out-perform other options, and as compilers improve this will only get better.

But it’s also easy for them to be incredibly expensive, creating unexpected copies of objects. The handling of nested lambdas is especially cloudy. And so far, many programmers seem to be leaping into Lambdas without realizing what the compiler has to do when captures are involved.

    auto lambda = [this, a, b, &c, d, e, f] (const T& t) -> bool {
        // do stuff
    };
    // ...
    lambda(t);

*may* be equivalent to

    struct Capture {
        self_type* m_this;
        int m_a;
        float m_b;
        string& m_c;
        int m_d;
        Object m_e; // maybe should have been by reference?
        bool m_f;
    };

    bool lambdaFn(void* param, const T& t) {
        const Capture& cap = *reinterpret_cast<Capture*>(param);
        // do stuff
    }

    //// Lambda invocation:
        // First an extra register/stack variable may be required
        // to store the function pointer.
        LambdaFn* lambda = lambdaFn;
        // Depending on context, 'new' may actually be invoked.
        Capture* cap = new Capture { this, a, b, c, d, e, f };
        lambda(cap, t);
        delete cap;

To put it in C# terms, imagine if “i = i + 1” resulted in unboxing/boxing if it’s written on a line number that’s a multiple of 3…

One of the good things about C/C++ is that it remains a low-level high-level language. Working with a GOOD compiler will produce generally straight forward code and you won’t be second guessing if the problems are coming from your end, or the way it was compiled.

auto (seriously?) is clearly NOT a keyword for the people who would be enjoying such a language. Even the pathetic favored argument about how it makes the code look “pretty” can be countered with the use of typedef. (And honestly, any decent IDE would have features to automatically fill out long types for you.)

Despite that case, Lambdas seem the be in the same boat… yes, javascript is a wonderful place to use them because you don’t really give a crap what happens when javascript compiles. In fact, javascript is designed for their use due to the way it handles functions; but javascript can do “strange” things if you don’t understand how it compiles the code. You can generate multiple instances of the same function by passing it around, or have multiple links to a single instance of that function.

The point is that this abstracts the code and passes on more tasks to the compiler to “fix your code” while you run around not knowing what you’re doing. You can hope that the compiler is going to “fix your code,” but compilers are dirt stupid; and when people start using lambdas for more than a line of code it WILL start creating problems.

Leave a Reply

Name and email address are required. Your email address will not be published.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

You may use these HTML tags and attributes:

<a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <pre> <q cite=""> <s> <strike> <strong> 

%d bloggers like this: