Why cannot a C++ compiler deduce the type of the lambda argument?

Viewed 219

I've got the following code, which can't be compiled

template <typename T>
void call_with(std::function<void(T)> f, T val) {
    f(val);
}

int main() {
    auto print = [](int x) { std::cout << x; };
    call_with(print, 42);
}

And the compilation error looks like

tst.cpp:17:2: error: no matching function for call to 'call_with'
        call_with(print, 42);
        ^~~~~~~~~
tst.cpp:11:6: note: candidate template ignored: could not match 'function<void (type-parameter-0-0)>' against
      '(lambda at tst.cpp:16:15)'
void call_with(std::function<void(T)> f, T val) {
     ^
1 error generated

I tried to compile it with g++ tst.cpp -o tst -std=c++17

So I know sometimes C++ can deduce template arguments under some conditions, but I'm wondering why it cannot compile this code in this case. It looks like there cannot be any other options for a type T to be anything, but int and for f to be anything but std::function<void(int)>.

1 Answers

Because print is not a std::function<void(int)>.And it can be converted to more than one valid type.

Try Run

    auto print = [](int x) { std::cout << x; };
    std::cout << typeid(print).name()<<"\n";
    std::function<void(unsigned __int64)> print_a = std::function<void(unsigned __int64)>(print);
    std::cout << typeid(print_a).name() << "\n";
    auto print_b = std::function<void(unsigned __int64)>(print);
    std::cout << typeid(print_b).name() << "\n";

You will get

class <lambda_d103cbf184cf5bdcf1494399ee6d564a>
class std::function<void __cdecl(unsigned __int64)>
class std::function<void __cdecl(unsigned __int64)>

Which shows print is a lambda,and it can be converted to many kinds of std::function<void(T)> like:

    auto print = [](int x) { std::cout << x; };
    auto print2 = std::function<void(unsigned __int64)>(print);
    auto print3 = std::function<void(unsigned short)>(print);
    auto print4 = std::function<void(char)>(print);

An exact std::function<void(int)> object can be deduced:

std::function<void(int)> print = [](int x) { std::cout << x; };
call_with(print, 42);

And 42 also need to be right type for deducing

std::function<void(int)> + int means T=int

std::function<void(short)> + short means T=short

but std::function<void(short)> + int can't be deduced. So you will get compile error on :

std::function<void(short)> print = [](short x) { std::cout << x; };
call_with(print, 42);

The right version is

std::function<void(short)> print = [](short x) { std::cout << x; };
call_with(print, short(42));
Related