Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can non-capturing lambdas not be default-constructed, and is it possible to work around this?

It is possible to use the type of a lambda as a template argument, like

template<typename InArg, typename Function>
class selfCompose {
  Function f;
 public:
  selfCompose(Function f): f(f) {}
  auto operator() (InArg x) -> decltype(f(f(x))) {
    return f(f(x));                              }
};

int main() {
  auto f = [](int x){return x*x;};
  std::cout << selfCompose<int, decltype(f)>(f)(4)  //  yields (4²)² = 256
            << std::endl;
  return 0;
}

However, this double use of f is kind of redundant. We can omit passing the lambda's type as the template (casting it to a suitable std::function (at loss of polymorphism – but C++ lambdas aren't parametrically polymorphic anyway)), however I have an application where I'd much prefer not having to pass its value to the constructor (because I'd like to use my class's initialisations themselves as a template parameter, where a particular constructor signature is expected). I'd like it to work like

template<class InArg, class Function>
class selfCompose {
  Function f;
 public:
  selfCompose() {}  // default constructor for f
  auto operator() (InArg x) -> decltype(f(f(x))) {
    return f(f(x));                              }
};

int main() {
  auto f = [](int x){return x*x;};
  std::cout << selfCompose<int, decltype(f)>()(4) << std::endl;
  return 0;
}

but this doesn't compile because lambdas have a deleted default constructor. Which is of course inevitable for capturing lambdas, but for simple ones like the one in my example this doesn't make much sense to me: they don't need to reference any local variables.

Is there some other way to get this functionality, or do I have to resort to defining the lambda old-fashionly as a named class?

struct myFun {
  auto operator() (int x) -> int {return x*x;}
};

(of course, the lambda functions I'd like to use aren't quite as simple as x → x², so just selecting from a few standard function classes wouldn't be flexible enough)

like image 266
leftaroundabout Avatar asked Oct 06 '22 23:10

leftaroundabout


1 Answers

You can follow the example of functions like make_pair and make_shared:

template<typename InArg, typename Function>
selfCompose<InArg, Function> make_selfCompose(Function f)
{
  return selfCompose<InArg, decltype(f)>(f);
}

int main() {
  auto f = [](int x){return x*x;};
  std::cout << make_selfCompose<int>(f)(4)
            << std::endl;
  return 0;
}
like image 63
Benjamin Lindley Avatar answered Oct 10 '22 02:10

Benjamin Lindley