Daily Dose C++: Lambdas (Part 2)

Source

Back To Basics: Lambda Expressions - Barbara Geller & Ansel Sermersheim - CppCon 2020

Starting from 31:50

C++20 Update

Lambda Expression Format:
[capture clause] <template parameters> (parameter list) specifier exception attribute -> return type requires {body}

  • specifier keyword:

    • mutable (C++11)
    • constexpr (C++17)
      • Deduced so keyword is optional.
      • Only reason, lambda express NEEDS to be constexpr.
      • Can constexpr be using in a lambda expression?
        • Yes, only if lambda is a generic type. Only in the body.
    • consteval (C++20)
      • similar to how const expr does in lexp
  • exception keyword:

    • noexcept: cannot throw an exception if noexcept keyword used in the exception spot of the l-expression format above.
  • attribute keyword:

    • Function attributes are not supported in lambda expressions.
      • nodiscard, deprecated, noreturn
    • Function type appears at the end of declaration, gnu::cdecl, gnu::regcall.
      • This is compiler specific.
  • requires keyword:

    • Lambda Expressions need to be generic
    • e.g. requires std::copyable\<T>

What's so great?

  • Easier to read
  • Convenient to write than a function object
  • Invoked immediately and not saved to a variable
  • Easier to use with algorithms and std::visit(), std::thread.
  • Designed well to work with those items.
  • Instead of writing a whole class and code, you just write the important parts and the compilers takes care of the rest.
    • Lambda expressions described as kind of backwards.

Callback

  • Can be passed as a function pointer, f obj, or closure.
  • Callback will be invoked by the receiver.
// Callback with STL Algorithms : std::count_if
int resB = std::count_if(data.begin(), data.end(), [](int i){return i>5;});

Maps

auto myLamb = [](const std::string &a, const std::string &b)
    {return a.size() > b.size();};

std::map<std::string, int, decltype(myLamb)> myMapB =
    {{string name, int size}, {"e.g.NameHere", 10}, ...}

Have to define as decltype and passed lambda expression as an auto decltype.

Generic Lambda

If auto is used for parameters declaration is what defines a generic lambda.
At least 1 parameter is defined as auto, then the whole lambda expression is generic.

auto myLamb = [](){} This auto is to define the closure type, not the auto defining the generic.

datatype myLamb = [](auto var1, int var2){} This auto is the one that defines a generic.

Capturing Structured Bindings

auto [x, y] = someFunction(); // Structured Bindings
auto myLamb = [x](){return x+7;} // Capturing x from Structured Binding

Technically illegal C++17. But some compilers will allow it.
If it fails to build, work around is to just set the capture clause as [x = x].

Resolved in C++20, but some compilers might not be up to date with this fix.

Extra Notes

  • No recursive lambdas, can't call itself.

  • C++11 Lambdas is more of a beta release. Preferred to be used in at least C++14.

  • Function pointers are more expensive to call than function object.

  • Beneficial if you capture the std::move(myPtr) and place it into a variable of a different name.

    • I'm guessing this is to reduce confusion. Since the value will no longer exist in the old variable.
    • Common 'gotcha' you need to be wary of.
  • Also related to the move action, be careful about mutable lambdas.

    • Try to use movable captures over mutable lambda expressions.

You'll only receive email when Wron publishes a new post

More fromĀ Wron