带有和不带有std::前缀的数学函数之间的另一个冲突

我知道这是一个非常古老的话题,但我不明白为什么它在这里表现得如此。

该程序:

#include <vector>
#include <algorithm>
#include <cmath>

int main()
{
    std::vector<double> a = {1,4,9,16};
    std::transform(a.begin(), a.end(), a.begin(), sqrt); // WORKS
    std::transform(a.begin(), a.end(), a.begin(), std::sqrt); // compilation error
}

编译错误是

g++ -std=c++17 -O3 main.cpp -Wall -Wextra -pedantic
main.cpp: In function ‘int main()’:
main.cpp:10:57: error: no matching function for call to ‘transform(std::vector<double>::iterator, std::vector<double>::iterator, std::vector<double>::iterator, <unresolved overloaded function type>)’
  std::transform(a.begin(), a.end(), a.begin(), std::sqrt); // compilation error
                                                         ^
........
g++ -std=c++17 -O3 main.cpp -Wall -Wextra -pedantic
main.cpp: In function ‘int main()’:
main.cpp:10:57: error: no matching function for call to ‘transform(std::vector<double>::iterator, std::vector<double>::iterator, std::vector<double>::iterator, <unresolved overloaded function type>)’
  std::transform(a.begin(), a.end(), a.begin(), std::sqrt); // compilation error
                                                         ^
........

所以,万一

  1. sqrt 编译器可以推导出参数/返回类型并使用此函数。
  2. std::sqrt编译器无法在std::sqrt?.. 的不同重载之间进行选择。

为什么会这样?编译时所有类型都是已知的,它到底不能推断出什么?为什么只是简单的sqrt作品(我找不到定义)?


以下可能表明正在发生某些事情:

当它说它不能理解std::sqrt没有上下文的类型时,这是有道理的。

所以,从这个剪断它可能遵循sqrt声明 smth 像

#include <cmath>

int main()
{
    typeid(sqrt); // compiles
    typeid(std::sqrt); // compilation error
}

whilestd::sqrt有许多重载,如https://en.cppreference.com/w/cpp/numeric/math/sqrt所示。

所以,我的问题是三重的:

  1. 是否在某处声明了全局数学函数?我想当你在没有它的情况下使用它std::时只是使用 ADL 或 smth 来找到它。另外,我不包括<math.h>.
  2. 如果sqrt在某些库中定义了这种情况,但我想要标准版本,如何处理?
  3. std::sqrt如果已知,为什么不能推导出参数/返回类型?

回答

错误消息的关键部分是“<未解析的重载函数类型>”。有多个版本std::sqrt采用不同的参数类型,编译器无法知道在该上下文中您需要哪一个。

显然,在全局命名空间中,您的编译器提供了 C 名称:

float sqrtf(float);
double sqrt(double);
long double sqrtl(long double);

只有一个名为 的函数sqrt,因此编译器知道要使用哪一个。

std命名空间中还有更多:

float sqrt(float);             // 1
float sqrtf(float);
double sqrt(double);           // 2
long double sqrt(long double); // 3
long double sqrtl(long double);
double sqrt(Integral type);    // 4

所以有四个名为 的函数std::sqrt,编译器没有任何规则可以选择一个。

要选择重载,您可以使用演员表(是的,这很不直观,而且坦率地说,很奇怪):

std::transform(a.begin(), a.end(), a.begin(), (double (*)(double))std::sqrt)

请注意,标记为“WORKS”的版本只能偶然使用。C++ 中不要求全局命名空间只有C 名称。


以上是带有和不带有std::前缀的数学函数之间的另一个冲突的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>