3

I'm getting the following error

min.cpp:17:30: error: no viable conversion from '<overloaded function type>' to 'Container::UnaryFun' (aka 'function<double (double)>')
    this->addFunction("abs", abs);

when trying to compile the following code:

#include <cmath>
#include <string>
#include <functional>

class Test
{
public:
  using UnaryFun  = std::function<double (double)>;

  Test()
  {
    this->addFunction("abs", abs);
  }
  auto addFunction(const std::string& name, UnaryFun fun) -> void
  {
    // ...
  }
};

auto main() -> int {
  Test eval;
  return 0;
}

I've tried to check the declaration of std::abs for argument double and return type double and looks like this:

inline _LIBCPP_INLINE_VISIBILITY double abs(double __lcpp_x) _NOEXCEPT {
  return __builtin_fabs(__lcpp_x);
}

in /usr/local/Cellar/llvm/15.0.7_1/include/c++/v1/stdlib.h.

It is accesible specifically for the double type. I've checked this by adding:

double a = 5;
double b = std::abs(a);

and this compiles without problems or conversion warnings.

I've tried to declare my own abs function like so:

inline double xabs(double val)
{
    return val < 0 ? -val : val;
}

and then change the following code like so to use this new xabs instead of std::abs

this->addFunction("abs", xabs);

and after this change, the code compiles.

Any ideas why the code with std::abs doesn't compile?

My environment: OS: Mac OS 12.6 Compiler:

Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

Command to compile: g++ -std=c++2a -o min min.cpp

Update based on comments

I dug a bit deeper, and it seems that there is a problem with how std::function is declared, which led to the problem above.

If I declare addFunction like so, without std::function, the problem disappears.

  auto addFunction(const std::string& name, double (*fun)(double)) -> void
  {
  }

This means that the compiler cannot figure out the matching abs if std::function is used but it can identify the matching overload if the type of the function is described directly without std::function.

RAllen
  • 1,235
  • 1
  • 7
  • 10
  • Does this answer your question? [How do I specify a pointer to an overloaded function?](https://stackoverflow.com/questions/2942426/how-do-i-specify-a-pointer-to-an-overloaded-function) – JaMiT Feb 18 '23 at 02:01
  • Thanks @JaMiT. No, it doesn't answer why the code above doesn't compile. See my update in the question itself. – RAllen Feb 18 '23 at 02:58
  • OK, you added an implicit cast to a function pointer (the type of the parameter). So you use an implicit conversion to specify which overload to use according to the function signature implied by the function pointer type. Hmm... Top answer from the proposed duplicate starts *"You can use `static_cast<>()` to specify which [overload] to use according to the function signature implied by the function pointer type"*. Are you being thrown because the answer did not cover all possible ways to specify a conversion? – JaMiT Feb 18 '23 at 03:40
  • @JaMiT, my question's goal is to identify why the code above doesn't compile. I haven't asked how to modify my code to make it work. The answer to my question is in my update, i.e. `std::function` doesn't allow to use overloaded functions as arguments. – RAllen Feb 18 '23 at 04:14
  • Oh right. You did ask for "why", not "how". My bad. *(I do wonder, though, why it took so much effort to get such a simple explanation from you. I covered the reason with 7 words, while you took an edit plus two comments.)* I should have been looking for [Why does the following `std::transform` example need a function pointer instead of a function object?](https://stackoverflow.com/q/56635291), [Overloaded pointer to function](https://stackoverflow.com/q/40217041), and [Why does std::sort fail to find the appropriate (static member) function overload?](https://stackoverflow.com/q/52475395) – JaMiT Feb 18 '23 at 06:22
  • *"The answer to my question is in my update,"* -- Questions are for questions. Answers should be posted as answers, not be edited into a question. – JaMiT Feb 18 '23 at 06:32

1 Answers1

6

The problem is that, since it has multiple overloads, std::abs doesn't have a single type. That means that the compiler can't select a std::function constructor to use to convert it since it can't deduce a type for the constructor's template parameter.

There are a couple of ways to get around that:

  1. Use a cast:
addFunction("abs", std::static_cast<double(*)(double)>(std::abs));
  1. Wrap it in a lambda:
addFunction("abs", [](double d) { return std::abs(d); });
  1. As you've done, wrap it in a non-overloaded function
Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
  • Thanks for the answer, Miles. These are nice workarounds to directly let the compiler know the type of the `std::abs` function we want to use. Of course, these approaches will work, but the question was why `g++` doesn't compile a combination of overloaded function and `std::function`. It seems that the answer is that the declaration of `std::function` itself doesn't allow us to use as an argument a function that has multiple overloads. Replacing `std::function` with an explicit declaration solves the problem. – RAllen Feb 18 '23 at 02:57
  • 1
    Like I said, `std::function`'s constructor is a template that can take any function-like type, but an overload set doesn't have a type. No type, no template type deduction, no viable constructor call. If `std::function` had an explicit `double(*)(double)` constructor it would work, but that's not how the class is designed. – Miles Budnek Feb 18 '23 at 03:13
  • 2
    `std::abs` is not addressable. The first snippet is wrong, although I'm not sure how bad it gets. – Passer By Feb 18 '23 at 05:13