Last Update:
Lambda Week: Tricks
Table of Contents
We’re on the last day of the lambda week. We have all the essential knowledge, and now we can learn some tricks!
The Series
This blog post is a part of the series on lambdas:
- The syntax changes (Tuesday 4th August)
- Capturing things (Wednesday 5th August)
- Going generic (Thursday 6th August)
- Tricks (Friday 5th August)(this post)
+[]()
Have a closer look:
#include <type_traits>
int main() {
auto funcPtr = +[]{};
static_assert(std::is_same<decltype(funcPtr), void (*)()>::value);
}
Please notice the strange syntax with +
. If you remove the plus sign, then the static_assert
fails. Why is that?
To understand how it works we can look at the output generated by the C++ Insights project. See the working example:
using FuncPtr_4 = void (*)();
FuncPtr_4 funcPtr =
+static_cast<void (*)()>(__la.operator __la::retType_4_18());
/* PASSED: static_assert(std::integral_constant<bool, 1>::value); */
// __la is __lambda_4_18 in cppinights
The code uses +
which is a unary operator. This operator can work on pointers, so the compiler converts our stateless lambda into a function pointer and then assigns it to funcPtr
. On the other hand, if you remove the plus, then funcPtr
is just a regular closure object, and that’s why the static_assert
fails.
While it’s probably not the best idea to write such a syntax with “+”, it has the same effect if you write static_cast
. You can apply this technique in a situation when you don’t want the compiler to create too many function instantiations.
IIFE - []()();
In most of the examples in the lambda series, you could notice that I defined a lambda and then call it later.
However, you can also invoke lambda immediately:
#include <iostream>
int main() {
int x = 1, y = 1;
[&]() noexcept { ++x; ++y; }(); // <-- call ()
std::cout << x << ", " << y;
}
As you can see above, the lambda is created and isn’t assigned to any closure object. But then it’s called with ()
. If you run the program, you can expect to see 2, 2
as the output.
This kind of expression might be useful when you have a complex initialisation of a const
object.
const auto val = []() {
/* several lines of code... */
}(); // call it!
Above, val
is a constant value of a type returned by lambda expression, i.e.:
// val1 is int
const auto val1 = []() { return 10; }();
// val2 is std::string
const auto val2 = []() -> std::string { return "ABC"; }();
You can see more in my separate article on that topic: Bartek’s coding blog: C++ Tricks: IIFE for Complex Variable Initialization.
Variadic Generic Lambdas and Fold Expression
Thanks to fold expressions in C++17 we can write even more compact code! For example we can write a simple print
utility that outputs the variadic argument list:
#include <iostream>
int main() {
const auto printer = [] (auto... args) {
(std::cout << ... << args) << '\n';
};
printer(1, 2, 3, "hello", 10.5f);
}
However, if you run the code it will print all arguments without any separator:
123hello10.5
To solve this issue, we can introduce a little helper and also fold over the comma operator rather than over <<
:
#include <iostream>
int main() {
const auto printer = [] (auto... args) {
const auto printElem = [](auto elem) {
std::cout << elem << ", ";
};
(printElem(args), ...);
std::cout << '\n';
};
printer(1, 2, 3, "hello", 10.5f);
}
And now we have the following output:
1, 2, 3, hello, 10.5,
This can be even shortened into:
const auto printer = [] (auto... args) {
((std::cout << args << ", "), ...);
std::cout << '\n';
};
And if we do not want to show the last comma at the end of the print sequence we can do the following:
#include <iostream>
int main() {
const auto printer = [] (auto first, auto... args) {
std::cout << first;
((std::cout << ", " << args), ...);
std::cout << '\n';
};
printer(1, 2, 3, "hello", 10.5f);
}
This time we need to use a generic template argument for the first entry and then a variadic parameter list for the rest. We can then print the first element and then add a comma before other entries. The code will now print:
1, 2, 3, hello, 10.5
Some More Interesting Cases
Some time ago I wrote a separate article about other aspects of lambdas, have a look: Bartek’s coding blog: 5 Curious C++ Lambda Examples: Recursion, constexpr, Containers and More.
Summary
Thanks for reading the whole series on Lambdas! We covered basic things, but I’m sure you can expand from this point easily.
- What’s your favourite “feature” of lambdas?
- What are your best use cases?
Let us know in comments below the article.
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 (all tiers get the book for free)
I've prepared a valuable bonus for you!
Learn all major features of recent C++ Standards on my Reference Cards!
Check it out here: