int abs(int) vs double abs(double)

5

我想要理解以下代码的行为,从C++标准角度来看(GCC 9.3,C++20):(代码链接)

#include <cstdlib>

template<class> struct type_tester;
int main() {
    type_tester<decltype(abs(0.1))>{};      // int abs(int) overload is selected for some reason!
    type_tester<decltype(std::abs(0.1))> {}; // double abs(double) overload is selected, as one would expect
}

所以,int abs(int) 被导入到全局命名空间,而 double abs(double) 没有被导入!
为什么?

这几乎肯定是一个 https://dev59.com/XXM_5IYBdhLWcg3wcSx_ 的重复问题。 - Andrew Henle
由于这是来自C标准库,我总是毫不犹豫地使用std::fabs来处理双精度浮点数,我不知道<cmath>引入了重载函数。 - MatG
C 没有重载,因此当您从全局命名空间调用 abs 时,您正在调用带有 int 参数的 C 函数。当您使用 std:: 命名空间时,您使用的是 C++,其中重载可用,并且存在 std::abs(double) - Marek R
https://godbolt.org/z/1jvY6z1vz - Marek R
@AndrewHenle 这是另一个问题。那里的答案表明,“C++头文件只是包含它并将所有内容放入std命名空间中”... - Igor R.
2个回答

6

cstdlib是C++版本的C标准库头文件stdlib.h。这些头文件必须在std命名空间中引入名称,并允许将它们引入全局命名空间中。 C标准库头文件中没有double abs(double),因此没有必要像对待C语言变种函数那样在全局命名空间中引入它。请注意,C语言没有命名空间,在全局命名空间中具有该函数可帮助与C代码的兼容性。 对于double abs(double)而言,这不是问题,因为该函数在C标准库头文件中不存在。

引自cppreference:

对于一些形式为xxx.h的C标准库头文件, C++标准库既包括一个同名头文件, 又包括另一个形式为cxxx的头文件 (所有有意义的cxxx头文件都列在上面)。 形式为xxx.h的头文件的预期用途仅用于互操作性。 可能需要C++源文件包含其中一个这些头文件才能有效地成为ISO C。 不打算也成为有效的ISO C源文件不应使用任何C头文件。

除了complex.h之外,C++标准库中包含的每个xxx.h头文件都会 将相应的cxxx头文件将放置在std命名空间中的名称 放置在全局命名空间中。

这些头文件还允许在std命名空间中声明相同的名称, 相应的cxxx头文件也允许在全局命名空间中声明相同的名称: 包括 肯定提供std::malloc并且可能也会提供::malloc。 包括 肯定提供std::malloc并且也可能提供 ::malloc。 这适用于甚至不是C标准库一部分的函数和函数重载。

这也适用于不属于C标准库的函数和函数重载。意思是:在全局命名空间中也可以具有double abs(double),但这不是必需的。


所以 cstdlib 为了兼容 C,将一些重载函数放在全局命名空间中? - Igor R.
@IgorR。是的。我添加了来自cppreference的相关段落。我想标准中可能会找到非常相似的措辞。 - 463035818_is_not_a_number
1
@IgorR。更确切地说,cstdlib将一些允许的重载放在全局命名空间中,以方便需要维护C和C++标准库的作者。 - Passer By

4
因为C++标准允许将int abs(int)导入到全局命名空间中。而double abs(double)不被导入全局命名空间,是因为C++标准没有要求这样做。引用标准文件中的规定,头文件cname的内容在除[library]、[thread]和[depr]之外与相应的C标准库头文件name.h相同。然而,在C++标准库中,除了在C中定义为宏的名称外,声明都处于命名空间std的命名空间范围内。未指定这些名称(包括在[support]到[thread]和[depr]中添加的任何重载)是否首先在全局命名空间范围内声明,然后再通过显式using-declarations([namespace.udecl])注入到命名空间std中。显然,您使用的C++标准库实现选择使用引用的规则描述的策略来处理C标准库函数,而不选择使用同样的策略处理C++标准库的重载。虽然这种具体结果不能保证符合标准,但它是符合标准的。C++标准库名称可能未在全局命名空间中声明,除非您使用已弃用的 C标准头文件。

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