What this means is that whenever you pass off a lambda to a function, and that lambda captures a variable, you need to be aware of whether or not the function keeps a reference to the lambda someplace, so that you'll know if it's safe to capture by reference or not. If you capture by reference but the function keeps a reference to the lambda, and it gets called after the captured variable has gone out of scope, you'll get into some nasty trouble.
And this is why I don't like the C++ spec as it stands. It requires a kind of global knowledge to work with correctly in local contexts, in such a way that the compiler can't really help you either (it may have been possible to annotate types to indicate closure lifetime, but it would be painful without more powerful type inference than C++ has).
You could get around this problem with spaghetti stacks or something, but you'd need to find a way to free the activation records that are floating around after their enclosing scopes have expired -- enter garbage collection which you do NOT want to require for C++.
That's the problem with Lisp, it's almost all-or-nothing. If you want to correctly include some of the benefits of the Lisp execution model -- like lambdas -- you need to accept the whole thing, lock, stock, and barrel. Including heap-allocated local vars and the garbage collector. (And yes -- Python, Ruby, Haskell, and Standard ML have a "Lisp execution model" in this sense.)
So we get compromises and hulking abominations like C++0x lambdas or -- worse yet -- their predecessor, Boost Lambdas.
tl;dr: Upward funargs are hard, let's go shopping.
Ultimately Garbage Collection would help there, but I am not sure we are going to see that in C++ anytime soon.
I just wish the actual passive-aggressive fight between the FSF and Apple would resolve and blocks could finally make it into upstream GCC C compiler.
I understand why the FSF wants copyright assignment, but it makes the process a lot longer and more complicated.
> [...]
> for_each is a template function, which means that it gets specialized for this particular type. This makes it an excellent candidate for inlining, and it's likely that an optimizing compiler will end up generating code for the above which would be just as good as the equivalent for loop.
This is somewhat unfair, as it seems to reflect the common misconception that (e.g.) C++ sort is faster than C qsort because it uses templates, when in fact qsort would be just as fast if its implementation were written in the .h file, as C++ sort's is. Compilers are perfectly capable of inlining calls to function pointers.
Calls to blocks should be able to be inlined too, but I guess they're still a new feature; I did a quick test, and it seems that gcc cannot inline them, but llvm-gcc and clang can.
In this case, the article suggests:
[array do:^(id obj) {
NSLog(@"Obj is %@", obj);
}];
Objective-C message calls are never inlined since they are dynamic, but if you write something like: static void for_each(NSArray *array, void (^callback)(id)) {
for(id obj in array) {
callback(obj);
}
}
[...]
for_each(array, ^(id obj){ NSLog(@"%@", obj); });
llvm-gcc and clang are able to generate code equivalent to doing the for loop directly.Does this happen even if the function that calls the callable (in this case sort) is not inlined? This would mean that the compiler generates a specialized function, say sort_{somerandomnumber} with the user-defined callable inlined into, and this seems kind of unlikely to me (while with templates the compiler is forced to do so).
In both your example the body of the caller (array do and for_each) is so small that I assume it is inlined.
Personally I prefer the C++0x closures precisely because of the reference/value capturing distinction.
tldr: Lambdas provide more flexibility but are more complicated to use. Blocks integrate with objective-C better and are simpler to use.