Live data from Hacker News

Lambda expression comparison between C++11, C++14 and C++17

maitesin.github.io

71–80 of 87 posts

Re: Lambda expression comparison between C++11, C++14 and C++17

#71

A question people who use C++ regularly, is C++ becoming easier to read and code?

Absolutely, and without a doubt. * `unique_ptr` as a local variable. Before C++11, I needed to either (a) define a holder class for anything that should be deleted at the end of a scope or (b) delete it manually and pray that there isn't an exception thrown. Now, I can just declare it, and trust the destructor to clean up after me. * `unique_ptr` as a return value. Previously, if a function returns a pointer, there w…

Re unique_ptr as a local variable: in C++98 you can use auto_ptr.

Re: Lambda expression comparison between C++11, C++14 and C++17

#72
post #66

Earlier quoted context omitted.

> I like the ability explicitly state what variables are being captured. Why? You state what variables are being captured by just using them in the lambda body.

> You state what variables are being captured by just using them in the lambda body. Wow, have you never spent a week debugging a JavaScript memory leak?

No. What I have done on the other hand was add unused lexical variables to an anonymous function so the runtime wouldn't optimise them out of the closure and I could still see them in the debugger.

Re: Lambda expression comparison between C++11, C++14 and C++17

#73
post #67

Earlier quoted context omitted.

I find that pointlessly pedantic. By the same measure, languages that don't offer bignums don't offer integers. Even Lisps need to implement closures in one way or another, and you may be surprised to see how they actually do it.

I think you miss the point, the OP was mentioning the property that in most languages, closures whose outer variables they are bringing into scope stay in scope (even if the outer function ends). In C++ they expire (is what I got from his comment, anyway).

Closed-by-value variables (I.e. the default) in C++ don't expire. Referenced or pointed-to object might, but this is completely consistent with the rest of the language.

Remember that C++ is a by value language. Pointers are explicit, fist class and distinct from the pointed to object.

References are werid, though.

Re: Lambda expression comparison between C++11, C++14 and C++17

#74
post #13

Earlier quoted context omitted.

How is it ugly? The capture list is a necessary complexity in a language with manual memory management.

The syntax is not the prettiest, but it is legible once you understand what [](){} means. In C#, there is no such thing, but there is a part of me that wishes we had such a thing. I like the ability explicitly state what variables are being captured.

PHP also doesn't capture variables by default (except $this) and i like it that way.

Re: Lambda expression comparison between C++11, C++14 and C++17

#75

There is another interesting feature in C++17 for lambdas: Possibility to cast a lambda to a function pointer. It will become possible to store a lambda as a struct/member that could bind to "this" (like in Javascript).

I assume this is only for stateless lambdas, that have not captured anything? Because otherwise a function pointer would not be enough, right? Currently, any stateless lambda can decompose into a function pointer and be passed to any function that expects a function pointer, right? Are you saying that ++17 has augmented this, and if so can you provide more details or a reference (or an example) as I'm quite curious t…

https://isocpp.org/files/papers/p0018r3.html

It's the best reference I found. The paper only talks of capturing "*this" by value (as in the original post of the topic).

I think I read that in a draft about coroutines. The idea was to capture "this" by reference and convert the lambda to a function pointer to make it movable.

It would useful for delegates or signals too.

Re: Lambda expression comparison between C++11, C++14 and C++17

#76
post #71

Earlier quoted context omitted.

Absolutely, and without a doubt. * `unique_ptr` as a local variable. Before C++11, I needed to either (a) define a holder class for anything that should be deleted at the end of a scope or (b) delete it manually and pray that there isn't an exception thrown. Now, I can just declare it, and trust the destructor to clean up after me. * `unique_ptr` as a return value. Previously, if a function returns a pointer, there w…

Re unique_ptr as a local variable: in C++98 you can use auto_ptr.

True, I didn't mention it, because it has its own issues. The move-on-copy semantics of auto_ptr makes it incompatible with std containers, and makes for some rather unexpected behavior.

Re: Lambda expression comparison between C++11, C++14 and C++17

#77

I love lambdas, but a lot of commenters are throwing around the word "closure" here, and c++ lambdas are definitely not closures. You can capture outside variables by value or by reference, but that value can expire before the lambda runs if the reference no longer exists; in which case, you are in trouble. Unlike a true closure (as in lisp or other languages), where the closed-over value stays around. If we talk onl…

You would have to combine copying and reference management, e.g. "[=]" and std::shared_ptr. It definitely requires the programmer to pay more attention though, compared to other languages/constructs.

Re: Lambda expression comparison between C++11, C++14 and C++17

#78
post #23

Another mildly obscure feature of lambdas is the ability to capture a variadic number of parameters. Example (slightly contrived): #include #include template void log(Args&&... args) { (std::cout std::future log_async(Args&&... args) { return std::async(std::launch::async, [args...] { log(args...); }); } int main() { auto f = log_async(1, 2, 3); f.wait(); }

For those of us still stuck on C++98 at work, would you mind explaining what's going on here? In particular, I can't figure out why the ellipsis is so separated from `args` here: (std::cout That looks like some black magic to me. The rest makes sense, I think.

Heh, great. I used to put an ellipsis into a programming example to mean “fill in whatever you actually do here”, and now C++ went and made it mean something. :)

Re: Lambda expression comparison between C++11, C++14 and C++17

#79
post #62

I love lambdas, but a lot of commenters are throwing around the word "closure" here, and c++ lambdas are definitely not closures. You can capture outside variables by value or by reference, but that value can expire before the lambda runs if the reference no longer exists; in which case, you are in trouble. Unlike a true closure (as in lisp or other languages), where the closed-over value stays around. If we talk onl…

I'm not familiar with closures from other languages. What about capturing a shared_ptr by value?

The thing to keep in mind is that copying a shared_ptr isn't cheap at all. It's a class with a pointer and atomic reference count inside and the atomic inc/dec takes many cycles.

Re: Lambda expression comparison between C++11, C++14 and C++17

#80

Earlier quoted context omitted.

Spores seem like an interesting solution. The language designer in me has a distaste for it, though :p For case 1 (capture of mutable references), an explicit copy operator might be better (as in, "I want whatever value this variable is bound to, rather than the storage location") (or even vice versa, where value is the default and there's an operator for location). In a way, spores accomplish this by forcing you to…

Agree on the copy operator, not only for spores, have wanted it more than one time in other languages too. Not sure how the this binding should work though. If calling a method you need to a) dispatch on the runtime type and b) provide the instance to the method when called.

The compiler would essentially emit the same code that it would in the case of the spore, but it would be automatic. You still get to dispatch on the runtime type, because the binding is created after the method invocation, but before the scope of the lambda to be closed.

I think when a programmer writes "foo.combobulate()", the vast majority of the time, the intend to capture "foo". If they didn't and were being clever, I don't think it's unreasonable for the compiler to expect them to be explicit and write "this.foo.combobulate()" instead. In the former case, the compiler creates the implicit binding to capture, in the latter it does nothing implicit and just closes over "this".

I'm certain that the compiler has enough information to do this, and that it's in accordance with the principle of least surprise ;)

Post reply on HN