zvrba
zvrba

Reputation: 24574

std::function constructor not found when calling a function

I have a function declared as follows; its exact working is not relevant for this.

template<typename T>
std::pair<int, int>
partition3(T *pT, const int N, const T &Kq, const int w,
           std::function<int(const T&, const T&, int)> P);

At the call site I attempt to do the following:

bool partition3_test()
{
  struct cmp
  {
    int operator()(int x, int y, int) const
    { return x-y; }
  };

  int V1[11] = { 3, 7, 1, 7, 7, 8, 10, 2, 16, 4, 3 },
      V2[11] = { 3, 6, 1, 6, 6, 8, 10, 2, 16, 4, 3 };

  std::function<int(const int&, const int&, int)> F = cmp();

  std::pair<int, int>
    p1  = partition3(V1, 11, 7, 0, cmp()),
    p2  = partition3(V2, 11, 7, 0, cmp());

  return false;
}

For the two calls of partition3 the compiler (MSVC 2010) complains that it could not deduce template argument for the last parameter. If I replace cmp() with F, the code compiles and works fine.

I have two questions:

  1. Why do I get the error? [compiler bug or some arcane C++ rule?]
  2. How can I achieve the same effect without first explicitly constructing F?

(Currently, I have solved the problem by introducing another template parameter on partition3 and declaring P as that template type.)

Upvotes: 1

Views: 301

Answers (1)

Ben Voigt
Ben Voigt

Reputation: 283793

cmp() isn't actually a std::function at all. The fact that the copy-initialization works may be confusing the issue, but that uses a converting constructor which must be using some sort of wrapper object, and I'm surprised that it works against a temporary functor object (ah, checking the standard it apparently make a copy of the functor).

On top of that, the function arguments don't match (pass-by-value and pass-by-const-reference are source-compatible, but not runtime-call-compatible), which again requires an adaptor.

The best solution is to make the template function more generic, so that it also works with raw function pointers and arbitrary functor objects, not just std::function:

template<typename T, typename Functor>
std::pair<int, int> partition3(T *pT, const int N, const T &Kq, const int w,
                               const Functor& P);

If for some reason you really wanted to use std::function (you need the virtual dispatch, for instance), you can. The error, quite contrary to what Nawaz wrote, is that this is a deducible context, but more than one type fits (this is where the adaptor/wrapper nature of std::function becomes important, because the type doesn't have to exactly match the parameter, it just has to be compatible. std::function<int(const long&, const long&, int)> would match just as well.)

Use instead a non-deducible context, that way the compiler won't even try to use the functor for deduction of T.

template<typename T, typename Functor>
std::pair<int, int> partition3(T *pT, const int N, const T &Kq, const int w,
                               std::function<std::identity<const T>::type&, std::identity<const T>::type&, int> P);

Upvotes: 4

Related Questions