Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic templates, type deduction and std::function

I'm trying to make a template function to which is possible to pass some other function with any type and number of parameters and bind it to a std::function. I managed to do this:

#include <iostream>
#include <functional>

int foo(int bar)
{
  std::cout << bar << std::endl;
  return bar;
}

template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
{
  return f;
}

int main()
{
  //auto barp = func(foo); // compilation error
  auto bar  = func(std::function<void (int)>(foo));

  bar (0); // prints 0
}

I would like to just call auto barp = func(foo); and have the types deduced, but this line gives the following compilation errors:

error: no matching function for call to ‘func(void (&)(int))’
    auto barp = func(foo);
                        ^
note: candidate is:
note: template<class Ret, class ... Args> std::function<_Res(_ArgTypes ...)> func(std::function<_Res(_ArgTypes ...)>)
 std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
                              ^
note:   template argument deduction/substitution failed:
note:   mismatched types ‘std::function<_Res(_ArgTypes ...)>’ and ‘int (*)(int)’
   auto barp = func(foo);
                       ^

Why is it trying to match std::function<_Res(_ArgTypes ...)> with int (*)(int)? I feel I should get the compiler somehow to expand _Res(_ArgTypes ...) to int(int), but how?

like image 566
Tarc Avatar asked Sep 27 '22 18:09

Tarc


1 Answers

A function is not an std::function, it is convertible to one. You can deduce the arguments of a function, however, barring ambiguity about overloads.

#include <iostream>
#include <functional>

int foo(int bar)
{
  std::cout << bar << std::endl;
  return 0;
}

// Will cause error.
//int foo(double); 

template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(Ret f(Args...))
{
  return f;
}

int main()
{
  auto bar = func(foo);
  bar (0); // prints 0
}

What you want to do with the original std::function is similar to this, which more obviously does not work:

template<typename T>
struct A
{
  A(T);  
};

template<typename T>
void func(A<T> a);

int main()
{
    func(42);
}

42 is not a A, it can be converted to one, though. However, converting it to one would require T to already be known.

like image 70
tahsmith Avatar answered Nov 15 '22 09:11

tahsmith