5

Context

In mathematics-related context, I'd like to define functors working on <cmath> functions. For the purpose of this question, we will be using std::invoke as our functor.

This is ill-formed (live demo):

std::invoke(std::sin, 0.0);

(g++-8.1) error: no matching function for call to 'invoke(<unresolved overloaded function type>, double)'

Indeed, std::sin is an overload set and the compiler lacks the type information to choose one of those functions.

Question

How could I name a specific function from an overload set? With what could we replace LEFT and RIGHT so that the following is well-formed and does what is expected (say, select double std::sin(double))?

#include <functional>
#include <cmath>

int main()
{
    (void) std::invoke(LEFT std::sin RIGHT, 0.0);
}

If this is not possible, is there a way to define a functor so it is overload-set-aware?

cpplearner
  • 10,463
  • 2
  • 34
  • 57
YSC
  • 34,418
  • 7
  • 80
  • 129
  • it does have to be in the style with `LEFT` and `RIGHT` or would you be fine with assigning `std::sin` to a function pointer or `std::function` and then use that to call `invoke` ? – 463035818_is_not_a_number Jan 14 '19 at 13:32
  • @user463035818 It needs to be nice at the caller site. `my_invoke(std::sin, 0)` is ok for instance. – YSC Jan 14 '19 at 13:33
  • Possible duplicate of [template type deduction failing (std::empty as a predicate)](https://stackoverflow.com/questions/50566936/template-type-deduction-failing-stdempty-as-a-predicate) – Robert Andrzejuk Jan 14 '19 at 13:58
  • @RobertAndrzejuk This is not the same issue: your proposed dup is a template deduction failure; my question is "how to name a specific function from an overload set". There is an overlap between the two, but its not the same. – YSC Jan 14 '19 at 14:07

2 Answers2

7

The easiest way I know to do this is to use a lambda to enable overload lookup

std::invoke([](auto val){return std::sin(val);}, 0.0);

Will allow you to pass any value to invoke and then the lambda body will handle the actual call and overload resolution will come in then.

You can use a macro to abstract the lambda body out of the call to invoke using something like

#define FUNCTORIZE(func) [](auto&&... val) noexcept(noexcept(func(std::forward<decltype(val)>(val)...))) -> decltype(auto) {return func(std::forward<decltype(val)>(val)...);}
//...
std::invoke(FUNCTORIZE(std::sin), 0.0);
NathanOliver
  • 150,499
  • 26
  • 240
  • 331
3

How could I name a specific function from an overload set?

static_cast. E.g.

std::invoke(static_cast< double(*)(double) >( &std::sin ), 0.0);

There are easier ways to do around this, e.g. use a generic lambda to avoid that horrible syntax:

std::invoke([](auto x){ return std::sin(x); }, 0.0);

In Qt we've been bit pretty hard by the problem of taking the address of overloaded functions up to the point that helpers have been introduced. I discussed a possible implementation of such a helper here.

Normative reference for the static_cast usage is here.

peppe
  • 19,728
  • 3
  • 47
  • 65