带和不带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
^
........

因此,在的情况下

  1. sqrt编译器可以推导参数/返回类型并使用此函数
  2. 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。

所以,我的问题有三个方面:

  1. 全局数学函数是在某处声明的吗?我想当你在没有std::的情况下使用它时,它只是使用ADL或smth来找到它。此外,我不包括<math.h>
  2. 如果sqrt在某个库中定义,但我想要标准版本,该如何处理这种情况
  3. 如果已知的话,为什么它不能推导出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的正确版本已经为您准备好了。

最新更新