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:

+[]()  

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: