标准重载的std::abs与std::function<double (double)>不匹配。

3

我遇到了以下错误

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

当尝试编译以下代码时:

#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;
}

我已经尝试检查 std::abs 的声明,对于参数 double 和返回类型 double,看起来像这样:

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

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

它仅适用于 double 类型。我已经通过添加以下内容进行了检查:

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

这段代码可以顺利编译,没有任何转换警告。

我尝试声明自己的abs函数,像这样:

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

然后将以下代码更改为使用这个新的xabs而不是std::abs

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

在这个更改之后,代码可以编译。

有任何想法为什么使用 std::abs 的代码无法编译?

我的环境: 操作系统:Mac OS 12.6 编译器:

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

编译命令:g++ -std=c++2a -o min min.cpp

基于评论的更新

我深入挖掘了一下,似乎是std::function的声明方式有问题,导致了上述问题。

如果我这样声明addFunction,不使用std::function,问题就消失了。

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

这意味着编译器无法在使用std::function时找到匹配的abs,但如果直接描述函数的类型而不使用std::function,它可以识别到匹配的重载版本。

这个回答解决了你的问题吗?如何指定一个重载函数的指针? - JaMiT
谢谢@JaMiT。不,这并没有回答为什么上面的代码无法编译。请看我在问题本身中的更新。 - RAllen
好的,您为函数指针(参数类型)添加了一个隐式转换。因此,您使用隐式转换来指定根据函数指针类型所暗示的函数签名使用哪个重载。嗯... 来自建议的重复问题的最佳答案开头是“您可以使用static_cast<>()来指定根据函数指针类型所暗示的函数签名使用哪个[重载]”。您是否因为答案没有涵盖所有可能的指定转换的方式而感到困惑? - JaMiT
@JaMiT,我的问题的目标是确定为什么上面的代码无法编译。我没有问如何修改我的代码使其工作。我的问题的答案在我的更新中,即std::function不允许使用重载函数作为参数。 - RAllen
哦,对了。你确实要求“为什么”,而不是“如何”。我的错。(不过我还是很好奇,为什么从你那里得到这样一个简单的解释需要这么多的努力。我用七个单词就解释清楚了原因,而你则需要一次编辑加上两条评论。)我应该寻找以下问题:为什么下面的std::transform示例需要函数指针而不是函数对象?重载函数指针为什么std::sort无法找到适当的(静态成员)函数重载? - JaMiT
我的问题的答案在我的更新中。问题是用来提问的,答案应该作为答案发布,而不是编辑到问题中。 - JaMiT
1个回答

6
问题在于,由于它有多个重载形式,std::abs没有一个单一的类型。这意味着编译器无法选择一个std::function构造函数来转换它,因为它无法推断构造函数的模板参数类型。
有几种方法可以避免这个问题:
  1. 使用强制类型转换:
addFunction("abs", std::static_cast<double(*)(double)>(std::abs));
  1. 将其封装在lambda中:
addFunction("abs", [](double d) { return std::abs(d); });

3. 正如你所做的那样,将其封装在一个非重载函数中。

谢谢你的回答,Miles。这些是不错的解决方法,可以直接让编译器知道我们想要使用的std::abs函数的类型。当然,这些方法都能够起作用,但问题是为什么g++不能编译重载函数和std::function的组合。看起来答案是std::function本身的声明不允许我们使用具有多个重载的函数作为参数。将std::function替换为显式声明可以解决这个问题。 - RAllen
1
就像我所说的那样,std::function的构造函数是一个模板,可以接受任何类似于函数的类型,但重载集合没有类型。没有类型,就没有模板类型推导,也就无法调用可行的构造函数。如果std::function<double(double)>有一个显式的double(*)(double)构造函数,那么它将起作用,但这不是该类的设计方式。 - Miles Budnek
2
std::abs不可寻址。第一个代码片段是错误的,尽管我不确定有多糟糕。 - Passer By

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接