Last Update:
Lambda Week: Syntax changes, C++11 to C++20
Table of Contents
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 starting since C++11 and what are the latest changes in C++20.
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 emptyspecifiers/exception/attr
-mutable
,noexcept
- additional specifiersret
- 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 emptyspecifiers/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
(you can capture a copy of the object obtained from dereferencing the this
pointer):
struct Baz {
auto foo() {
return [*this] { std::cout << s << std::endl; };
}
std::string s;
};
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 pass 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 emptyspecifiers/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: Lambda Week: Capturing Things - C++ Stories.
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 Print, or @Amazon Full Colour Print
- Buy together with my C++17 Book Buy C++17 in Detail AND C++ Lambda Story Together
- Support me on Patreon Become a Patron
I've prepared a valuable bonus for you!
Learn all major features of recent C++ Standards on my Reference Cards!
Check it out here: