带有和不带有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
^
........
所以,万一
sqrt编译器可以推导出参数/返回类型并使用此函数。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所示。
所以,我的问题是三重的:
- 是否在某处声明了全局数学函数?我想当你在没有它的情况下使用它
std::时只是使用 ADL 或 smth 来找到它。另外,我不包括<math.h>. - 如果
sqrt在某些库中定义了这种情况,但我想要标准版本,如何处理? 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 名称。