3

If i have a template function in C++11, is it then possible to wrap it in a std::function?

My problem is like this:

I have a generic function (say a sum function) where the return type depends on the types of the arguments:

template <typename T>
auto sum(T const& a, T const& b) -> decltype(a+b)
{
    return a + b;
}

and I want to wrap it in a std:function for specific argument types (say int). I'm trying the following:

int main()
{
  std::function<int(int,int)> intsum = std::bind(static_cast<int(*)(int,int)>(&sum), std::placeholders::_1, std::placeholders::_2);
  auto mysum = intsum(2, 4);

  std::cout << mysum << std::endl;

  return 0;
}

But compiling the above code produces an error:

error: invalid static_cast from type ‘<unresolved overloaded function type>’ to type ‘int (*)(int, int)

Is there some way to achieve what I am trying to do?

AcId
  • 458
  • 2
  • 12

3 Answers3

4

You can just specialize the template and use that:

std::function<int(int,int)> intsum = sum<int>;
auto mysum = intsum(2, 4);

std::cout << mysum << std::endl;
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
2

You can't wrap a generic function in a specific instance of std::function, which is what you asked for.

You can, however, wrap a fully-specified instance of a generic function, which is what your code is actually trying to do:

template <typename T>
auto sum(T const& a, T const& b) -> decltype(a+b)
{
  return a + b;
}

#include <functional>
#include <iostream>

int main()
{
  std::function<int(int,int)> intsum =
    std::bind(sum<int>,
              std::placeholders::_1, std::placeholders::_2);
  auto mysum = intsum(2, 4);

  std::cout << mysum << std::endl;
}

Or more simply

  std::function<int(int,int)> intsum = sum<int>;

Or of course if you don't really need to wrap it, just:

  auto mysum = sum(2, 4);
Useless
  • 64,155
  • 6
  • 88
  • 132
1

Your problem is you got the type wrong.

There is no int(int,int) overload of sum<?>. So when you static_cast<int(*)(int,int)(sum), it complains that it cannot do the conversion.

There is a int(int const&,int const&) overload. If you static_cast<int(*)(int const&, int const&)>(sum) it would work.

Conversion of a function template to a pointer to function won't work if there is no such overload to point to.

There are three approaches that are reasonable.

First, cast to the right type. static_cast<int(*)(int const&,int const&)>(sum).

Second, explicitly specialize. sum<int> instead of the static_cast expression.

Third, create an overload set object:

struct sum_t{
  template<class...Args>
  auto operator()(Args&&...args)const
  -> decltype(sum(std::declval<Args>()...))
  {
    return sum(std::forward<Args>(args)...);
  }
};
static sum_t sum_os;

and replace static_cast<int(*)(int,int)>(sum) with sum_os.

sum_os can be passed to bind or std::function or whatever, and the overload resolution is deferred until the invocation code.

As a bonus, it is easier for the compiler to optimize than a function pointer call.

As a negative, perfect forwarding is imperfect.

A C++14 version of the overload set is:

[](auto&&...args)->
decltype(sum(decltype(args)(args)...))
{ return sum(decltype(args)(args)...); }

which can be turned into a macro:

#define OVERLOAD_SET(FUNC) \
  [](auto&&...args)-> \
  decltype(FUNC(decltype(args)(args)...)) \
  { return FUNC(decltype(args)(args)...); }

which lets you simply do OVERLOAD_SET(sum) at the point of use, and pass in the entire overload set.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • why not `->decltype(auto)` in the C++14 lambda version? – Piotr Skotnicki Mar 13 '15 at 08:40
  • @piotrs. SFINAE does not work with return type deduction. I want to be able to ask if a call is a valid one. – Yakk - Adam Nevraumont Mar 13 '15 at 10:40
  • ah, but how can you benefit from SFINAE with a single lambda expression? I mean, there can't be any fallback, can be? – Piotr Skotnicki Mar 13 '15 at 10:44
  • @piotrs. The consumer can ask if a given expression is a valid call, and have their own fallbacks. As an example, if someone fixed `std::function`'s ctor (bad std bad library) to only accept things you can actualky invoke with the signature, a pair of overloads `std::function` and `std::function` could be dispatched between. – Yakk - Adam Nevraumont Mar 13 '15 at 10:49