3

I'm curious as to why the following template deduction doesn't work (VS2015):

template<typename T>
class Foo
{
public:
  template<typename U>
  U get(U u) { return u; }

  // Default constructed U and T for example only.
  template<typename U>
  U get(std::function<U(U, T)> f) { return f(U(), T()); }

  template<typename U>
  U get(U u, std::function<U(U,T)> f) { return u; }

};

In the example above, the following is successful:

Foo<int> f;
auto f_asInt = f.get(5);      // f_asInt is of type int
auto f_asFloat = f.get(5.0f); // f_asFloat is of type float.

auto ff_asInt = f.get([](int, int) { return 5; });
auto ff_asFloat = f.get([](float, int) { return 5.0f; });

The following however fails to compile

Foo<int> f;
auto f_asInt = f.get(5, [](int, int) { return 5; });
auto f_asFloat = f.get(5.0f, [](float, int) { return 5.0f; });

I'm getting the following error:

error C2784: 'U Foo<int>::get(U,std::function<U(U,T)>)': could not deduce template argument for 'std::function<U(U,T)>' from 'main::<lambda_c4fa8cb1e6fa86997f25b7dabd5d415f>'

If I spell out the whole template, it works as expected.

Foo<int> f;
auto f_asInt = f.get<int>(5, [](int, int) { return 5; });
auto f_asFloat = f.get<float>(5.0f, [](float, int) { return 5.0f; });

I would like for the template arguments to be deduced in this case, is that even possible?

lcs
  • 4,227
  • 17
  • 36

1 Answers1

4

It doesn't work because a std::function type can't be deduced from a lambda. The compiler has no way of knowing for what F a std::function<F> can be constructed from a given lambda. The last two examples in the first block only work because the get(U u) overload is being used, not the one with a std::function argument.

If you want to be able to accept lambdas then you need to have an overload that accepts a generic callable type rather than a std::function. It's hard to give more specific direction unless you explain what you're trying to do.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312