Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type of a lambda function, using auto

I am trying to write a c++ lambda function, and don't like having to use auto as the type. Currently it looks like:

#include <iostream>

int main() {
//  Sends the address of an integer to a function which prints out the contents;
    auto print_int = [](int* a) {std::cout << *a << std::endl;};
    int a;
    a = 3;
    print_int(&a);
    return 0;
}

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

  • What is the type of a lambda function?

seems relevant, but I am not sure how to adapt it. Thanks.

like image 444
oliversm Avatar asked Dec 13 '16 12:12

oliversm


People also ask

What is the type of a lambda function?

Lambda expressions in C++14 are functions that can be treated as any other object, such as a class or struct. They can utilize variables defined in the same scope, 'capturing' them implicitly or explicitly by value or reference. A lambda object (also known as 'closure object') can be called like a normal function.

How many types of lambda expressions are there?

Types of Lambda Body In Java, the lambda body is of two types. () -> System. out. println("Lambdas are great");

What is the return type of lambda expression?

The return type of a method in which lambda expression used in a return statement must be a functional interface.

Which type of object is created in a lambda expression?

Yes, any lambda expression is an object in Java. It is an instance of a functional interface. We have assigned a lambda expression to any variable and pass it like any other object.


3 Answers

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.

like image 65
Guillaume Racicot Avatar answered Oct 18 '22 20:10

Guillaume Racicot


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.
like image 27
eerorika Avatar answered Oct 18 '22 20:10

eerorika


The lambda has its own unnamed type.

Your may convert your lambda into std::function<void(int*)>
(or for captureless lambda to void (*)(int*)).

You may create your functor explicitly so you give it a name, something like:

class Print_int
{
public:
    void operator()(int* a) const {std::cout << *a << std::endl;}
};

and then

Print_int print_int;
like image 38
Jarod42 Avatar answered Oct 18 '22 22:10

Jarod42