Ganil
Ganil

Reputation: 519

Type of lambdas in C++

I am trying to learn lambdas in C++, but stumbled on something I can't quite understand.

Here is code:

#include <iostream>

typedef double Func(double); 

double applyFunc(Func f, double x)
{
    return f(x);
}

int main()
{
    std::cout << applyFunc([](double x) {return x + 1;}, 3) << std::endl;
}

Now this works just fine (prints "4"), i.e. type of the lambda expression used is exactly double (*)(double).

But if I add closure to the lambda expression, like:

int main()
{
    int n = 5;
    std::cout << applyFunc([n](double x) {return x + n;}, 3) << std::endl;
}

Then I get an error from the compiler:

In function ‘int main()’:
error: cannot convert ‘main()::__lambda0’ to ‘double (*)(double)’ for argument ‘1’ to ‘double applyFunc(double (*)(double), double)’
  3) << std::endl;
   ^

And I don't understand why is that. I mean, from the point of view of applyFunc() it still receives a pointer to a function taking double argument and returning double, and it doesn't know that we used variable 'n' from context, so the type of lambda expression should be the same, as in the first example, right?

I would very appreciate help, thank you in advance!

Upvotes: 2

Views: 266

Answers (2)

Shafik Yaghmour
Shafik Yaghmour

Reputation: 158459

A lambda is convertable to a function pointer only if it does not have a capture, we can see this by going to the draft standard section 5.1.2 Lambda expressions which says (emphasis mine):

The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

This is an alternative solution which does not rely on this conversion:

template <typename Callable>
double applyFunc(Callable f, double x)
{
    return f(x);
}

Update

If you are interested in the difference between using std::function and templates then you should read std::function vs template. There are many good answers there and a lot of food for thought.

Upvotes: 4

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153810

Each lambda expression returns an object with a distinct type. If the lambda expression does not capture any variables, it can be converted to a a function pointer type of an appropriate signature (i.e., the return type and the arguments need to agree with those of the lambda expression). When there is a variable captured, the entity created can't be represented by a plain function. Instead, the lambda expression yields an object of class type with a function call operator. That is, your second code uses a lambda expression yielding an object of a class which is roughly equivalent to this:

class lambda {
     int n;
public:
     lambda(int n): n(n) {}
     double operator()(double x) const { return x + n; }
};

Upvotes: 2

Related Questions