0

I was playing with function pointers vs. std::function and came across the following problem.

Let's consider the folowing code:

#include <cmath>
#include <functional>

// g++ -std=c++17 SF.C -o SF
// clang++ -std=c++17 SF.C -o SF

int main()
{
    typedef double (*TpFunctionPointer)(double) ;

    TpFunctionPointer pf1 = sin;                     // o.k.
    TpFunctionPointer pf2 = std::sin;                // o.k
    TpFunctionPointer pf3 = std::riemann_zeta;       // o.k

    std::function< double(double) > sf1( sin );                // o.k
    std::function< double(double) > sf2( std::sin );           // fails
    std::function< double(double) > sf3( std::riemann_zeta );  // fails
}

Compiling with g++ v8.2 or clang v7.0 works fine for the function pointer pf1, pf2, pf3, and for sf1. However for sf2 and sf3 I get a rather long error messages, e.g.:

SF.C:17:47: error: no matching function for call to ‘std::function<double(double)>::function(<unresolved overloaded function type>)’
  std::function< double(double)> sf2( std::sin );           // fails

Is this intended behavior?
Shouldn't sf2 and sf3 be fine?

genpfault
  • 51,148
  • 11
  • 85
  • 139
Peter
  • 23
  • 3
  • Actually, even the first one fails for me (hence not an answer for now + what LRIO said) – Matthieu Brucher Jan 14 '19 at 13:39
  • 2
    funny that this is very much related to a question asked just a couple of seconds ago: https://stackoverflow.com/questions/54182502/call-a-functor-with-a-specific-function-from-an-overload-set – 463035818_is_not_an_ai Jan 14 '19 at 13:41
  • The difference is that `std::function`'s constructor is templated, so the compiler has no context to deduce an overload from. – molbdnilo Jan 14 '19 at 13:45
  • Even more fun is that `double (*pf4)(double) = static_cast(std::sin);` doesn't compile, but `std::function sf4 (static_cast(std::sin)); ` does. – molbdnilo Jan 14 '19 at 13:47
  • @molbdnilo Off the top of my head I can't remember how well-defined that is. My gut tells me this is a case of "compiles but isn't legal" but I'd almost put money on it being esoterically correct. Either way a good reason not to use it IMO! – Lightness Races in Orbit Jan 14 '19 at 13:58
  • @LightnessRacesinOrbit: I think it compiles because `std::function` will perform the necessary conversions (from `double` to `float` and back). It's pretty clear why it's legal, but you of course lose accuracy. Mind you, that can be intentional (if you don't need that accuracy). – MSalters Jan 14 '19 at 14:05
  • @MSalters Hopefully that is the case, rather than a silent (and invalid) fptr cast – Lightness Races in Orbit Jan 14 '19 at 14:07
  • @LightnessRacesinOrbit: Well, the cast from overload set to function pointer is basic overload resolution. That's not the risky part. As for the second part (inside `std::function`), that needs to treat the function pointer as a callable object. `std::function` can't just cast a callable object to `double(*) (double)`, e.g. capturing lambda's or any other stateful object can't be cast like that. – MSalters Jan 14 '19 at 14:13

1 Answers1

5

There are multiple overloads of the <cmath> std::sin (there's a template version in <complex>, but that's not what you want), and the compiler doesn't know which one you want, despite the fact that only one will successfully bind to your std::function type! C++ doesn't do lookup backwards in that sense…

…except when it does! An exception is static_cast on a function pointer type, which is exactly what you need here:

std::function<double(double)> sf2(static_cast<double(*)(double)>(&std::sin));

There's an example of this on the static_cast cppreference documentation page.

Some potential improvements over this general solution (thanks to Nathan and MSalters):

std::function<double(double)> sf2(static_cast<TpFunctionPointer>(&std::sin))

or

std::function<double(double)> sf2([](double val){ return std::sin(val); });
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    Could also use `[](double val){return std::sin(val);}` if you don't want to remember the function pointer syntax. – NathanOliver Jan 14 '19 at 13:46
  • 1
    Suggestion: the solution might be more readable using the typedef from the question: `std::function sf2(static_cast(&std::sin))` – MSalters Jan 14 '19 at 13:51
  • @MSalters As is that! – Lightness Races in Orbit Jan 14 '19 at 13:56
  • Thanks for the suggestions. – Peter Jan 14 '19 at 14:45
  • I understand that I can work around the problem with lambdas or casts. The problem with lambdas is, that you actually hide a simple assignement in a lengthy piece of code. Also the casts make the code less readable.I actually want to the three argument special functions like assoc_laguerre or assoc_legendre which makes these solutions even less readable. Therefore my question, wether this behavior is intentional by the standard and has some purpose, or just happens with both implementatations, g++ and clang++. – Peter Jan 14 '19 at 14:54
  • @Peter This is all standard. I'm not quite prepared to say that it "has some purpose", except that the alternative would require a substantial hack to the lookup rules, and having that on `static_cast` is messy enough. – Lightness Races in Orbit Jan 14 '19 at 14:56
  • @ Lightness Races in Orbit: o.k., so this just happened during the evolution of C++. I think I'll switch my implementation from std::function to plain function pointers to make the code more readable, although I intended it the other way. Again thanks. – Peter Jan 14 '19 at 15:06