16

The following program is illegal, and I would like to understand why:

#include <functional>
#include <iostream>

template<typename Result, typename Arg>
void deduce(std::function<Result(Arg)> f)
{
  std::cout << "Result: " << typeid(Result).name() << std::endl;
  std::cout << "Arg: " << typeid(Arg).name() << std::endl;
}


int main()
{
  auto f = [](int x)
  {
    return x + 1;
  };

  deduce(f);

  return 0;
}

clang's output:

$ clang -std=c++11 test.cpp 
test.cpp:48:3: error: no matching function for call to 'deduce'
  deduce(f);
  ^~~~~~
test.cpp:26:6: note: candidate template ignored: could not match 'function<type-parameter-0-1 (type-parameter-0-0)>' against '<lambda at test.cpp:34:13>'
void deduce(std::function<T2(T1)> f)
     ^
1 error generated.

It seems like I ought to be able to convert my lambda to the std::function received by deduce. Why is it not possible for the compiler to apply an appropriate conversion in this case?

Praetorian
  • 106,671
  • 19
  • 240
  • 328
Jared Hoberock
  • 11,118
  • 3
  • 40
  • 76

1 Answers1

13

The problem is that while a lambda that takes an int and returns an int can be converted into a std::function<int(int)>, its type is not std::function<int(int)> but an arbitrary implementation-defined type I think.

You can work around this by telling the compiler what types you want. Then the conversion will happen as expected.

auto f = [](int x){ return x + 1; };
deduce<int, int>(f);  // now ok

Alternatively, be explicit on the static type of f.

std::function<int(int)> f = [](int x){ return x + 1; };
deduce(f);  // now also ok
5gon12eder
  • 24,280
  • 5
  • 45
  • 92
  • Thanks. I understand the types are not the same, but I'm trying to understand why the compiler is not able to apply an appropriate conversion here. – Jared Hoberock Jan 08 '15 at 23:30
  • 2
    The problem is not the conversion of the argument value but the deduction of the template parameters. Once they are known (as in the first snippet I gave), the conversion is fine. I'll try whether I can look up the exact rules that are in effect here. – 5gon12eder Jan 08 '15 at 23:35
  • 1
    Yes, the deduction is the problem. It mst be an exact deduction, because otherwise, everything goes: Any specialization may, or may not, have the neccessary conversion. – Deduplicator Jan 08 '15 at 23:37
  • 2
    The definition of a lambda function isn't completely arbitrary, for the record, but the gist of the answer is right. @Jared, the compiler cannot construct a `std::function` (this is just a class template), but it can construct a `std::function` for concrete `Result` and `Arg`. If you did give concrete types for these (such as int and int as 5gon12eder does), then it has a constructor that it can use to turn your lambda object into an instance of the class. But without types, it has no constructors to try because it has no type. And you can't just deduce these types in general. – GManNickG Jan 08 '15 at 23:39