AcId
AcId

Reputation: 458

In C++11, is it possible to wrap a template function in a std::function?

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?

Upvotes: 3

Views: 1028

Answers (3)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275730

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.

Upvotes: 1

Useless
Useless

Reputation: 67772

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);

Upvotes: 2

TartanLlama
TartanLlama

Reputation: 65630

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;

Upvotes: 4

Related Questions