Live data from Hacker News

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

maitesin.github.io

21–30 of 87 posts

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

#21
post #19

Earlier quoted context omitted.

How would you do the equivalent of this in Rust? auto on_heap = std::make_unique (...); function_that_accepts_lambda([obj = std::move(on_heap)]() { obj->bar(...); })

let on_heap = Box::new(...); function_that_accepts_lambda(move || { on_heap.bar(); }); This is sort of what kibwen was mentioning: move is a single annotation that overrides everything to capture by value rather than have it inferred.

What if you want to move some things, but copy others?

e.g.

  auto shared = std::make_shared(...);
  auto unique = std::make_unique(...);

  function_that_accepts_lambda([shared, u = std::move(unique)] {
      shared->foo(...); u->bar(...);
  });

  // Outer scope can still use shared.
  shared->foo(....);

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

#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();
  }

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

#24
post #21

Earlier quoted context omitted.

let on_heap = Box::new(...); function_that_accepts_lambda(move || { on_heap.bar(); }); This is sort of what kibwen was mentioning: move is a single annotation that overrides everything to capture by value rather than have it inferred.

What if you want to move some things, but copy others? e.g. auto shared = std::make_shared (...); auto unique = std::make_unique (...); function_that_accepts_lambda([shared, u = std::move(unique)] { shared->foo(...); u->bar(...); }); // Outer scope can still use shared. shared->foo(....);

If the type implements Copy they'll be implicitly copied when moved into the Rust closure(I think). Or you can declare a scope var and clone() manually.

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

#25
post #18
post #16

Earlier quoted context omitted.

Very often the capture list is empty, it could have been elided (as the parameter list can be) if the syntax could have been made unambiguous.

Well, you need something to indicate the beginning of a lambda. So you can think of "[]" as serving that role, instead of "lambda" in Python or "\" in Haskell.

You can use C# fat arrow syntax, it even allows removing the braces for single expression lambdas which is the most common from anyway.

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

#26

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 was no way on knowing who was responsible for calling `delete`. Now, I can clearly indicate intent. `unique_ptr` means that the caller now owns the object, while C-style pointer or reference means that the callee still owns the object.

* With lambda statements, I can call `std::sort` in-place, with the sorting criteria immediately visible. Previously, I would need to define a function elsewhere in the code, obscuring what may be a simple `a.param * With range-based for loops, I can loop over any container without needing the very long `std::vector::iterator` declaration.

* `= delete` to remove an automatically generated method, such as copy constructors. Previously, you would declare that method to be private, then never make an implementation of it. `= delete` shows your intent much more clearly.

* `static_assert`, so that you can bail out of templates earlier, and with reasonable error messages.

* Variadic templates. These aren't needed in 99% of cases, but they are incredibly useful when designing libraries.

* `std::thread` No more messing around with different thread libraries depending on which platform you are on.

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

#27
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(); }

Which standard version?

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

#28
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(); }

Which standard version?

That snippet depends on fold expressions, which are in c++17. AFAIK, capturing a variadic parameter pack should work in C++11.

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

#29
post #5

This is the first time I've looked at C++ lambdas. They appear magnificently powerful and also like another pile of easy ways to get completely screwed up. Ah well, that's just the C++ way I suppose. Makes me glad for Rust, that's for sure!

Here take your rustwin point! I really like the explicit capture of C++'s lambdas more than the implicit one in most other languages (C#, Java, Python...) where you easily ends-up with a closure not referencing the expected variable. See: https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/clos...

I was burned by the same thing in Javascript.

"Explicit is better than implicit". Therefore, I agree with you that explicit closure list, with the ability to copy and reference captured variables, is actually what C++ does right, not wrong.

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

#30
post #21

Earlier quoted context omitted.

let on_heap = Box::new(...); function_that_accepts_lambda(move || { on_heap.bar(); }); This is sort of what kibwen was mentioning: move is a single annotation that overrides everything to capture by value rather than have it inferred.

What if you want to move some things, but copy others? e.g. auto shared = std::make_shared (...); auto unique = std::make_unique (...); function_that_accepts_lambda([shared, u = std::move(unique)] { shared->foo(...); u->bar(...); }); // Outer scope can still use shared. shared->foo(....);

I am 99% sure this is identical:

    let on_heap = Box::new(...);
    let shared = Arc::new(...);

    let s = shared.clone();
    function_that_accepts_lambda(move || {
        on_heap.bar();
        s.foo();
    });
We have to make the extra s binding.

Also, my sibling is correct that Copy types will just be copied, not moved.

Post reply on HN