3
votes

So I've been learning about templates and lambdas so I tried to combine the two. I am trying to create a templated typedef that specifies what the lambda is supposed to return and accept. It works well for non capturing lambdas but it cannot handle any lambdas that capture. This is my code for now:

template<typename T>
using L  = T(*)(T);

template<typename T>
T applyLambda(T var, L<T> lambda){
  return lambda(var);
}

int main() {
  
  const int a = 45;

  int b = applyLambda<int>(3, [] (int x)->int { // non capturing lambda
    return x+5;
  });

  std::cout << b << std::endl;

}

But if I change the function call to this:

int b = applyLambda<int>(3, [=] (int x)->int {
    return x+a;
  });

I get the following error:

candidate function template not viable: no known conversion from
      '(lambda at main.cpp:15:31)' to 'L<int>' (aka 'int (*)(int)') for 2nd argument
T applyLambda(T var, L<T> lambda)

Now my question is what do I need to change here:

template<typename T>
using L  = T(*)(T);

In order to accept lambdas that capture.

I do realize I could always do it like this but that would not specify lambda return type and parameters

template<typename T, typename LF>
T applyLambda(T var, LF lambda){
2

2 Answers

5
votes

L is decalred as function pointer type. Lambdas without capture list could convert to function pointer implicitly, but lambdas with capture list can't.

This user-defined conversion function is only defined if the capture list of the lambda-expression is empty.

You can use std::function instead.

template<typename T>
using L  = std::function<T(T)>;

LIVE

BTW: You can use std::type_identity (since C++20) to exclude the 2nd function argument of applyLambda from deduction (see non deduced context for details), then template argument deduction would work for you and no need to specify the template argument again. This is true for the function pointer version too. (If your compiler doesn't support C++20 it's quite easy to make your own type_identity.) E.g.

template<typename T>
T applyLambda(T var, std::type_identity_t<L<T>> lambda){
  return lambda(var);
}

then

int b = applyLambda(3, [=] (int x)->int {
  return x+a;
});

LIVE

1
votes

You need to use std::function objects instead of naked lambdas.

Unfortunately for some complex strange reasons each lambda is a different type on its own and there is no easy way to declare a variable or a parameter of type lambda. Even if you succeed (e.g. using decltype) the variable you declare is useless because you can only assign it the specific lambda you got the type from and not any another one.

In other words:

void bar() {
    int x = 0;
    auto inc1 = [&x](){ x++; };
    auto inc2 = [&x](){ x++; };
    inc1 = inc2; // Error, type is different despite EVERYTHING being identical
}

std::function is designed to solve this limitation so excluding passing them to template functions (and understanding the implications of that) you should never use parameters of type lambda, and use std::function objects instead.