我知道这是一个非常古老的话题,但我不明白为什么它在这里会这样。
程序:
#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
^
........
因此,在的情况下
sqrt
编译器可以推导参数/返回类型并使用此函数std::sqrt
编译器无法在std::sqrt
?的不同重载之间进行选择
为什么会这样?在编译过程中,所有类型都是已知的,它究竟不能推断出什么?为什么只是普通的sqrt
有效(我找不到定义)?
以下可能表明发生了什么:
#include <cmath>
int main()
{
typeid(sqrt); // compiles
typeid(std::sqrt); // compilation error
}
当它说没有上下文就无法理解std::sqrt
的类型时,这是有道理的。
因此,从这个片段中可以看出,sqrt
被声明为类似的smth
//global namespace
double sqrt(double);
而CCD_ 7具有许多过载,如https://en.cppreference.com/w/cpp/numeric/math/sqrt。
所以,我的问题有三个方面:
- 全局数学函数是在某处声明的吗?我想当你在没有
std::
的情况下使用它时,它只是使用ADL或smth来找到它。此外,我不包括<math.h>
- 如果
sqrt
在某个库中定义,但我想要标准版本,该如何处理这种情况 - 如果已知的话,为什么它不能推导出
std::sqrt
的参数/返回类型
错误消息的关键部分是"lt;未解析的重载函数类型>";。有多个版本的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)
注意,标记为";作品";工作只是偶然。C++中没有要求全局命名空间只有C名称。
全局数学函数是否在某处声明?我想当你在没有
std::
的情况下使用它时,它只是使用ADL或smth来找到它。此外,我不包括<math.h>
。
<cmath>
可能包括也可能不包括<math.h>
,并因此暴露Csqrt
函数。这就是您的实现正在做的事情。
如果在某些库中定义了
sqrt
,但我想要标准版本,该如何处理这种情况?
如果需要std::sqrt
,请使用std::sqrt
。如果您这样做,将不会使用其他功能。
如果已知的话,为什么不能推导出
std::sqrt
的参数/返回类型?
与sqrt
的C版本不同,std::sqrt
的过载
float sqrt ( float arg );
double sqrt ( double arg );
long double sqrt ( long double arg );
double sqrt ( IntegralType arg );
并且编译器没有在将CCD_ 27传递给CCD_ 28时使用哪个重载的规则。为了解决这个问题,我们可以使用lambda将对sqrt
的调用移动到编译器可以预加载重解析的上下文中。看起来像
std::transform(a.begin(),
a.end(),
a.begin(),
[](auto val){ return std::sqrt(val); });
现在,std::sqrt
的正确版本已经为您准备好了。