Live data from Hacker News

The Evolutions of Lambdas in C++14, C++17 and C++20

fluentcpp.com

41–50 of 75 posts

Re: The Evolutions of Lambdas in C++14, C++17 and C++20

#41

Earlier quoted context omitted.

Problems with std::function:- 1. It can not hold move only callable objects. 2. It heap allocate stored callable object if the object is large enough.

Can you point to better alternate ways or idioms?

stlab has a task type that works around a couple of these issues: https://stlab.cc/libraries/concurrency/task/task/

Re: The Evolutions of Lambdas in C++14, C++17 and C++20

#42
post #25

i was in a leetcode BS interview a while ago with a googler who thought i could not write code in c++ because i could not remember lambda syntax. i only use maybe 30% of c++ features in my software and have been doing this for over 20 years. it’s only recently i started using lambdas more, but still stay away from them because of the potential hidden allocations.

If you're not capturing variables that allocate memory (like std::vector or std::string), there will be no allocations. And if you're that sensitive about hidden allocations, chances are that you're going to avoid most of these STL types in the first place (no allocating constructors or RAII), so you won't probably need to worry about this. Even the people using C-like C++ ("Orthodox" C++) seem to use lambdas liberally when it make sense.

(Or maybe you're talking about putting a lambda into an std::function, which is a totally different thing and will probably incur heap allocation?)

Re: The Evolutions of Lambdas in C++14, C++17 and C++20

#43

Is there any flag in some c++ compiler that forces to you to use the newer features? For example, forbidding non-auto declarations.

There is tooling along those lines. I wouldn't expect to find a tool that forbids non-auto declarations, but there are certainly some that suggest when something could've been auto.

Right. clang-tidy lets you specify rules you’d like it to enforce in the code you pass through it. It’s not a part of the compilation process, but uses the same underlying driver (if you’re building with clang)

Re: The Evolutions of Lambdas in C++14, C++17 and C++20

#44

Is there any flag in some c++ compiler that forces to you to use the newer features? For example, forbidding non-auto declarations.

Quite apart from not being possible forbidding non-auto declarations also sounds like a terrible idea. I am not really that fond of the auto keyword. In some cases it is necessary and/or helpful but it is also very helpful to be able to tell what the type of a variable is at a glance. I think the auto keyword should be used sparingly.

Re: The Evolutions of Lambdas in C++14, C++17 and C++20

#45
post #27
post #23

> Even if you don’t need to handle several types, this can be useful to avoid repetition and make the code more compact and readable. ... > namespace1::namespace2::namespace3::ACertainTypeOfWidget Deeply nested namespaces are problematic in themselves due to namespace resolution (e.g. see https://abseil.io/tips/130 ), but templates should never be an answer to "my type is too long to type out". You're hurting yoursel…

Yes, deeply nested namespaces are not nice. I think boost really dropped the ball on this. boost::asio::ip::tcp should have been boost::tcp. Are they trying to be as bad as java?

As the article explains it's not "as bad as Java" it's worse, what Java does actually makes sense in Java, maybe you have to type slightly more characters but Java is already a verbose language best suited to heavy tool-assist. However it doesn't make sense in C++ because the benefits Java gets don't apply and the price is heavier.

There are a bunch of things like this in C++ where superficially C++ feature X is like feature Y in another language, and so C++ programmers wrongly assume the problems with feature X must also plague feature Y. I'm sure the reverse happens too.

Re: The Evolutions of Lambdas in C++14, C++17 and C++20

#46

Earlier quoted context omitted.

Dynamic languages often benefit from some tool assistance. Both your and GPs experience can happen at the same time: GP is probably using a linter or an IDE (like Pycharm) which will point out that "myVariablr" may be undefined.

If you're returning myVariable instead, that's "fine" - it is always defined. You might get a warning that myVariabl_r_ is unused, but that's also not a given. The bar for being 'used' is pretty low. For example, in this def main(): x = 10 l = [] l.append(x) return 5 pyflakes believes both x and l are "used", even though neither affects function return value.

In Python even the trivial definition of "used" (i.e. "bound name which is referred to afterwards") is not that easy (e.g. getattr), linters go for the literal definition instead ("bound name, which is statically referred to afterwards"). This works for 99 % of code. Your definition of "used" (~"influences the observable result") is of course fully within the dominion of Rice's theorem, though this particular example would be caught by any data-flow analyzer.

Re: The Evolutions of Lambdas in C++14, C++17 and C++20

#47

Earlier quoted context omitted.

That are used to hold captured variables and values. Internally, lambdas are function pointers + the context, and the context may or may not be dynamically allocated depending on how lambdas are used.

Internally, lambdas are structs with the "function call" operator overload. Context is done via members of the struct: capture by-value, and the struct copies them and stores its own, capture by reference, and the struct member is a reference. There should be zero heap allocation in any case.

You capture by value a variable that allocates and you get a heap allocation.

Re: The Evolutions of Lambdas in C++14, C++17 and C++20

#48

Earlier quoted context omitted.

Internally, lambdas are structs with the "function call" operator overload. Context is done via members of the struct: capture by-value, and the struct copies them and stores its own, capture by reference, and the struct member is a reference. There should be zero heap allocation in any case.

You capture by value a variable that allocates and you get a heap allocation.

That's dependent on the captured type's copy (or move) behavior, and is completely different from your claim that how the lambda is used can influence this. Lambdas themselves never heap allocate.

Re: The Evolutions of Lambdas in C++14, C++17 and C++20

#50
post #22

Earlier quoted context omitted.

I've seen a variation of this bug several times in just the last year: myVariable = 1 if not something_unusual(): myVariablr = 2 return myVariablr This code will work just fine until something_unusual() returns True and then it crashes with UnboundLocalError: local variable 'myVariablr' referenced before assignment This really sucks when it happens during a long-running job. Say for example you're looping through an…

Those type of errors will be caught by the compiler. It's definitely a problem for python-like languages (but not for compiled languages) because there's no compilation step involved before running the code.

It’s a compiled language in the same sense that Java is compiled. Most people use the CPython implementation which compiles .py files to .pyc files before executing them. That compiler can’t catch bugs of this form because it’s not always a bug. That’s just how dynamic languages work.

But if Python had a different syntax like “let var = value” for creating new variables rather than mutating existing ones, we wouldn’t have this specific problem. That’s what this thread is about.

If C++ inferred “auto” for all assignments/initializations, it would have a similar problem where typos in assignments effectively lead to that line of code getting skipped.

Post reply on HN