2

I would like to create a std::function from an overloaded template function. Compiling with g++ -std=c++14 I obtain an overload resolution error. I have a hack to massage the function template into a form which the compiler recognises, but I would like to know if there are more elegant approaches. Below is code illustrating the error and my hack,

#include <functional>

template <typename T>
T foo(T t) { return t; }

template <typename T>
T foo(T t1, T t2){ return t1 + t2; }

int main (){
    //error: conversion from ‘<unresolved overloaded function type>’ 
    //to non-scalar type ‘std::function<double(double)>’ requested
    std::function<double(double)> afunc = &foo<double>; 

    //my workaround to 'show' compiler which template 
    //function to instantiate 
    double (*kmfunc1)(double) = &foo<double>;
    std::function<double(double)> afunc = kmfunc1;  
}

I have two questions

  • Is it unreasonable of me to expect the compiler to resolve which template to use ?
  • What is the most elegant way to create the std::function is the above situation?
newling
  • 624
  • 6
  • 10
  • 2
    possible duplicate of [Wrap overloaded function via std::function](http://stackoverflow.com/questions/10111042/wrap-overloaded-function-via-stdfunction) – m.s. Jul 15 '15 at 11:30
  • It is also possible to use `[](double d){return f(d);}` instead of the cast, or a slightly more elegant cast via `std::function afunc = static_cast(foo);` (though that cast is too explicit IMHO) or even `template std::function make_function(Sig* f) { return {f}; } auto afunc = make_function(foo);` – dyp Jul 15 '15 at 11:34
  • 1
    As an aside, this is one of the reasons why I think `std::function` should have a `Sig* pf` constructor, and why my `std::function`-likes have such an overload. – Yakk - Adam Nevraumont Jul 15 '15 at 18:08

2 Answers2

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

creates a single object that represents the complete (global) overload set of its argument. (I use ... as macros do not understand all uses of , in modern C++)

std::function<double(double)> afunc = OVERLOAD_SET(foo<double>);
std::function<double(double,double)> bfunc = OVERLOAD_SET(foo<double>);

both should work, as should

std::function<double(double)> afunc = OVERLOAD_SET(foo);
std::function<double(double,double)> bfunc = OVERLOAD_SET(foo);

while we are at it. The idea here is we defer overload resolution until the point where the arguments are determined.

OVERLOAD_SET(foo) compiles to:

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

which is a stateless lambda that returns whatever invoking foo on its arguments would do. It uses perfect forwarding, with its usual imperfections.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1
  • Is it unreasonable of me to expect the compiler to resolve which template to use ?

The compiler is not allowed to resolve which template to use based on the C++ language rules. The relevant std::function constructor is:

template< class F > 
function( F f );

And, according to [temp.deduct.type]:

If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

The non-deduced contexts are:
— [...]
— A function parameter for which argument deduction cannot be done because the associated function argument is a function, or a set of overloaded functions (13.4), and one or more of the following apply:
    — more than one function matches the function parameter type (resulting in an ambiguous deduction), or
    — no function matches the function parameter type, or
    — the set of functions supplied as an argument contains one or more function templates.
— [...]

So passing in &foo<double> is a non-deduced context, template deduction fails, hence the compile error.

  • What is the most elegant way to create the std::function is the above situation?

I would just use a cast:

std::function<double(double)> afunc = 
    static_cast<double(*)(double)>(&foo<double>);

and then complain to the standards committee about why std::function<Sig> doesn't have a constructor that takes a Sig*. In whatever order you want.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thanks Barry. There's still something I don't get though. If it's true that **more than one function matches the function parameter type** why, when the first template function is commented out, does the code fail to compile, with same error? (of course in this second case compilation should fail as the second template function is not compatible with `double(double)` – newling Jul 16 '15 at 13:11
  • @newling The compile error itself is misleading, but the fact that it would fail to compile is correct. Note that `auto afunc = &foo;` will compile if you comment out one or the other `foo`, which means that template deduction succeeded. `template function(F )` only participates in overload resolution if `F` is callable with the right args and the right return type. – Barry Jul 16 '15 at 13:30