Type of a lambda function, using auto

Lambdas are meant to be used with either auto or as template parameter. You never know the type of a lambda and you can't type it. Each lambda has it's own unique type. Even if you knew the name of the type, their type names usually contains character prohibited in type names.

Why does lambda have their own type? because in reality, the compiler create a class defined a bit like that:

struct /* unnamed */ {

    // function body
    auto operator()(int* a) const {
        std::cout << *a << std::endl;
    }

} print_int; // <- instance name

This code is very close to an equivalent (I omitted conversion operator). As you can see, you already use auto, because lambdas are deducing the return type.

Some will say to use std::function<void(int*)>, but I disagree. std::function is a polymorphic wrapper around anything callable. Since lambdas are callable types, they fit into it. I other words, it work much like std::any but with a call operator. It will induce overhead in your application.

So what should you do?

use auto! auto isn't bad. In fact, it can even make your code faster and reduce unnecessary typing. If you feel uncomfortable with auto, well you shouldn't! auto is great, especially if you don't have the choice ;)

In fact, you could avoid using auto by using a template parameter:

template<typename F, typename Arg>
void parametric_print(F function, Arg&& arg) {
    function(std::forward<Arg>(arg));
}

Then use it like this:

int main() {
    int a = 3;
    parametric_print([](int* a) {std::cout << *a << std::endl;}, &a);
}

There you go, no auto! However, a template parameter is deduced with the same rule as auto. In fact, concept are accepted into the C++20 standard with terse function templates. You could write the same function template like this:

// C++20
void parametric_print(auto function, auto&& arg) {
    function(std::forward<decltype(arg)>(arg));
}

As mentionned by Oktalist, if concepts are accepted into the standard, then you could replace auto with Callable:

Callable print_int = [](int* a) { std::cout << *a << std::endl; };

But it does not result in a different type, it just enforce some rules when deducing the type.


Here you go:

int a;
[](int* a) {std::cout << *a << std::endl;}(&a);

No auto used.


However, I would like to change the auto to something like std::function<void(int)> but am not sure how.

That is certainly possible. std::functional has a templated converting constructor. You simply pass the lambda to the constructor and that's all there is to it. Also, you'll need to fix the argument type since your lambda expects an int*, not an int (this is the kind of bug that auto fixes automatically).

std::function<void(int*)> print_int = [](int* a) {std::cout << *a << std::endl;};

Note however that there are two drawbacks to this.

  • std::function wrapper is an indirection layer that hides the actual type of the callable object. This wrapper layer adds runtime overhead and prevents some optimizations.
  • You have to manually update the type if you make changes to the return value, or arguments of the lambda.