Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Standard overloaded std::abs doesn't match std::function<double (double)>

Tags:

c++

std

macos

g++

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.

like image 807
RAllen Avatar asked Jan 27 '26 09:01

RAllen


1 Answers

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
like image 123
Miles Budnek Avatar answered Jan 30 '26 00:01

Miles Budnek