C++ Lambda Week: Syntax changes, C++11 to C++20
Let’s start the week with Lambda Expressions. The plan is to have a set of concise articles presenting core elements of lambda expressions. Today you can see how the syntax has evolved since C++11 and what the latest changes in C++20 are.
The Series
This blog post is a part of the series on lambdas:
- The syntax changes (Tuesday 4th August) (this post)
- Capturing things (Wednesday 5th August)
- Going generic (Thursday 6th August)
- Tricks (Friday 5th August)
Syntax in C++11
The first iteration of lambdas!
In a basic form they have the following syntax:
[]() specifiers exception attr -> ret { /*code; */ }
-
[]
- introduces the lambda expression, capture clause -
()
- the list of arguments, like in a regular function, optional if specifiers/exception list is empty -
specifiers/exception/attr
-mutable
,noexcept
- additional specifiers -
ret
- trailing return type, in most cases not needed as the compiler can deduce the type -
/* code; */
- the body of the lambda
You can read the spec located under N3337 - the final draft of C++11: [expr.prim.lambda] .
Some example:
// 1. the simplest lambda: []{}; // 2. with two params: [](float f, int a) { return a * f; }; [](int a, int b) { return a < b; }; // 3. trailing return type: [](MyClass t) -> int { auto a = t.compute(); print(a); return a; }; // 4. additional specifiers: [x](int a, int b) mutable { ++x; return a < b; }; [](float param) noexcept { return param*param; }; [x](int a, int b) mutable noexcept { ++x; return a < b; }; // 5. optional () [x] { std::cout << x; }; // no () needed [x] mutable { ++x; }; // won't compile! [x]() mutable { ++x; }; // fine - () required before mutable [] noexcept { }; // won't compile! []() noexcept { }; // fine
Syntax in C++14
In C++14 the “high level” syntax hasn’t changed much, but the capture clause allows you perform “capture with initialiser”, and the parameter list can take auto
arguments (it means generic lambdas).
Additionally, the return type of a lambda expression follows the rules of a regular function return type deduction ( auto
), so in short, compilers are smarter now.
You can see the specification in N4140 and lambdas: [expr.prim.lambda] .
Some examples:
The first one with a capture with an initialiser:
#include <iostream> int main() { int x = 30; int y = 12; const auto foo = [z = x + y]() { std::cout << z << '\n'; }; x = 0; y = 0; foo(); }
As you can see above the compiler can now create member variables for closure type from expressions like z = x + y
.
And another significant change is a generic lambda which supports auto
as an argument.
const auto foo = [](auto x, auto y) { /*...*/ };
Syntax in C++17
Since C++17 you can now use constexpr
as an additional specifier for the lambda.
[]() specifiers exception attr -> ret { /*code; */ }
-
[]
- introduces the lambda expression, capture clause -
()
- the list of arguments, like in a regular function, optional if specifiers/exception list is empty -
specifiers/exception/attr
-mutable
,noexcept
,constexpr
-
ret
- trailing return type -
/* code; */
- the body of the lambda
Some example:
constexpr auto Square = [](int n) { return n * n; }; // implicit constexpr static_assert(Square(2) == 4);
And additionally the capture syntax supports *this
:
struct Baz { auto foo() { return [*this] { std::cout << s << std::endl; }; } std::string s; };
Another thing to have in mind is that in C++17 the dynamic exception specification is removed so in practice you can only use noexcept
to mark functions and lambdas.
Syntax in C++20
Since C++20 you can now use consteval
as an additional specifier for the lambda, and what’s more, you can specify a template tail!
[]<tparams>() specifiers exception attr -> ret requires { /*code; */ }
-
[]
- introduces the lambda expression, capture clause -
<tparams>
- template tail, template arguments -
()
- the list of arguments, like in a regular function, optional if specifiers/exception list is empty -
specifiers/exception/attr
-mutable
,noexcept
,constexpr
,consteval
-
ret
- trailing return type -
/* code; */
- the body of the lambda
Some examples:
int main() { const int x = 10; auto lam = [](int x) consteval { return x + x; }; return lam(x); }
Template lambdas and perfect forwarding:
auto ForwardToTestFunc = []<typename ...T>(T&& ...args) { return TestFunc(std::forward<T>(args)...); };
Next time
In the next article, you’ll see how to capture things from the external scope. See here: todo, tomorrow :)
See More in Lambda Story
If you like to know more, you can see my book on Lambdas! Here are the options on how to get it and join 1000 of readers:
- Buy directly at Leanpub: C++ Lambda Story @Leanpub
- Buy at Amazon, Kindle version: C++ Lambda Story, Kindle
- Buy together with my C++17 Book Buy C++17 in Detail AND C++ Lambda Story Together
- Support me on Patreon Become a Patron
- 1Password for Linux development preview
- C Lambda Week: Syntax changes, C 11 to C 20
- Learning Rust: Mindsets and Expectations
- Racket v7.8
- Setting up a privacy-oriented Home Lab
- Real-time noise suppression for video conferencing
- Anachro-PC - The Anachronistic Personal Computer
- Visualizing Citation Cartels
- Do Vue 3 refs admit a monad instance?
- There’s a Hole in the Boot
- You don’t need SMS-2FA
- Enum or Trait Object
- Espressif ESP32: Bypassing Secure Boot using EMFI
- A Brief Opinionated Overview of NIST’s Post-Quantum Cryptography Round 3 Candidates
- Socialism’s DIY Computer
- Bug Bounty Platforms vs. GDPR: A Case Study
- Verified seL4 on secure RISC-V processors
- Leaving Google Analytics is Finally Plausible
- Oil Summer 2020 Blog Roadmap
- What’s the difference between a good QA director and a great one? A comparison