Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template deduction from lambda

Tags:

c++

templates

I have a function which takes two std::functions as arguments. The parameter of the second function has the same type as the result of the first.

I wrote a function template like this:

template<typename ResultType>
void examplFunction(std::function<ResultType()> func, std::function<void(ResultType)> func2) {
  auto x = func();
  func2(x);
}

I can call it with:

void f() {
  examplFunction<int>([]() { return 1; },   //
                      [](int v) { std::cout << "result is " << v << std::endl; });
}

Is there a way to to get rid of the <int> at examplFunction<int> and let the compiler deduce the type of ResultType?

like image 783
ErWu Avatar asked Sep 07 '18 15:09

ErWu


People also ask

Can you template a lambda?

Lambda-expressions are not allowed in unevaluated expressions, template arguments, alias declarations, typedef declarations, and anywhere in a function (or function template) declaration except the function body and the function's default arguments.

How do you pass lambda to a function?

Passing Lambda Expressions as ArgumentsIf you pass an integer as an argument to a function, you must have an int or Integer parameter. If you are passing an instance of a class as a parameter, you must specify the class name or the object class as a parameter to hold the object.

What is the correct syntax for Lambda Expression in C++11?

Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. auto y = [] (int first, int second) { return first + second; };

How do you write a lambda function in CPP?

Creating a Lambda Expression in C++auto greet = []() { // lambda function body }; Here, [] is called the lambda introducer which denotes the start of the lambda expression. () is called the parameter list which is similar to the () operator of a normal function.


3 Answers

Do you actually need std::function in there? std::function is useful when you need type erasure. With templates, you can usually skip it altogether:

template<class F1, class F2>
void examplFunction(F1 func, F2 func2, decltype(func2(func()))* sfinae = nullptr) {
  auto x = func();
  func2(x);
}

The sfinae parameter makes sure the function can only be called with functions such that func2 can be called with the result of func.

like image 178
Angew is no longer proud of SO Avatar answered Oct 22 '22 00:10

Angew is no longer proud of SO


Yes, there is.

template<typename ResultType>
void examplFunction_impl(std::function<ResultType()> func, std::function<void(ResultType)> func2) {
    auto x = func();
    func2(x);
}

template<class F1, class F2>
void examplFunction(F1&& f1, F2&& f2)
{
    using ResultType = decltype(f1());
    examplFunction_impl<ResultType>(std::forward<F1>(f1), std::forward<F2>(f2));
}

Demo

In this case you require that f1 be invocable with no arguments, so you can figure out the return type in the helper function. Then you call the real function while explicitly specifying that return type.

You could add some SFINAE to make sure this function only participates in overload resolution when f1 can indeed be invoked like that (and if f2 can also be invoked with the return value of f1).

Although I have to agree with @Angew that in the given example there is no need for std::function. That might of course be different in a real-world situation.

like image 13
Max Langhof Avatar answered Oct 22 '22 00:10

Max Langhof


std::function has a templated (and otherwise unconstrained) constructor, so deducing it from simply an argument type is not that easy a deal. If those arguments still need to be std::functions, you can skip one <int> for the price of two std::functions, and let deduction guides do the rest:

void f() {
    examplFunction(std::function([]() { return 1; }),   //
                   std::function([](int v) { std::cout << "result is "
                                             << v << std::endl; }));
}

A fun fact is that this does not always work. For instance, the current implementation of libc++ lacks guides for std::function, thus violating the standard.

like image 3
bipll Avatar answered Oct 22 '22 00:10

bipll