Why is lambda not converted to function in this case?

Viewed 98

I'm writing code with parameter packs and std::function. The goal is to be able to pass a function and a pack of parameters into a function, and be able to call the function of that pack (and do some other work). Here is the stripped down example of what I want:

#include <functional>
#include <iostream>

template<typename... Args>
using MyFunc = std::function< void(Args...) >;

template<typename... Args>
void call(MyFunc<Args...> f, Args... args) {
    f(args...);
}

int main() {
    auto lambda = [](int a, double b) {std::cout << a << b << std::endl;};
    call<int, double>(lambda, 1, 2.0);
    // but this works?
    //call(MyFunc<int, double>(lambda), 1, 2.0);

    return 0;
}

When I compile this with clang 13.0 I get: could not match 'function<void (int, double, type-parameter-0-0...)>' against '(lambda at pack.cpp:13:19)'. For g++ 7.5 I get a similar: ‘main()::<lambda(int, double)>’ is not derived from ‘std::function<void(Args ...)>’.

As you can see, I commented an example that works: explicitly converting the argument to std::function. But why can't it do it implicitly?

P.S. I can't use a templated F instead of std::function for a reason that is out of scope of this simple example.

1 Answers

The problem is template argument deduction for Args on the 1st function parameter f fails since implicit conversion (from lambda to std::function) won't be considered in the deduction.

You can use std::type_identity (since C++20; it's quite easy to write one for pre-C++20) to exclude f from deduction. E.g.

template<typename... Args>
void call(std::type_identity_t<MyFunc<Args...>> f, Args... args) {
    f(args...);
}

BTW even template arguments are specified template argument deduction is still performed to determine the end of parameter pack. And in this case you don't need specify them, you can just call it as call(lambda, 1, 2.0);.

Related