为什么 <cmath> 在 std 命名空间之外公开实体?

Why does <cmath> expose entities outside the std namespace?

据我了解,C++ 使用的 C-entities 与 <math.h> 中的那些一样,可以通过包含相应的 <c...> 以安全的方式包含在 std 命名空间中] 变体(显然除了宏)。 cppreference seems to confirm this.

但是,包括 <cmath> 似乎是在 log 函数 外部 std 命名空间:

#include <cmath>

namespace log {}

int main() {}

使用 g++ -Wall -Wextra -pedantic -std=c++17 a.cpp 编译得到:

a.cpp:3:11: error: ‘namespace log { }’ redeclared as different kind of entity
    3 | namespace log {}
      |           ^~~
In file included from /usr/include/features.h:446,
                 from /usr/include/x86_64-linux-gnu/c++/9/bits/os_defines.h:39,
                 from /usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h:524,
                 from /usr/include/c++/9/cmath:41,
                 from a.cpp:1:
/usr/include/x86_64-linux-gnu/bits/mathcalls.h:104:1: note: previous declaration ‘double log(double)’
  104 | __MATHCALL_VEC (log,, (_Mdouble_ __x));
      | ^~~~~~~~~~~~~~

我的标准库坏了吗?我可以做些什么来避免这种情况吗?


我最初是使用 <random> 偶然发现的,这意味着更多 headers 可能会受到明显随机 C-entities 的影响,这些 C-entities 在整个顶级命名空间中喷涌而出。

也许拥有更多 C/C++ 标准知识的人会证明我是错的,但对于 C++ 编译器也能够处理 C 代码,C 库中的函数需要在命名空间之外,实际上你应该只找到包含在 extern "C" 中的 C-Library 定义,这样你就可以在你的 C++ 代码中透明地使用旧的 C 函数

Why does <cmath> expose entities outside the std namespace?

因为历史。

<cmath> header 是从 C 标准库(这里命名为 <math.h>)继承而来的 header。在C语言中,只有声明所有名称的全局命名空间1

由于许多 C++ 实现也是 C 实现,它们通常通过包含 as-is2 来实现继承的 C 标准 header,这意味着它声明全局名称。

虽然可能有一些技术可以避免(标准宏的情况除外)声明全局名称,但自标准化之前就一直这样做的实现不太可能改变行为,因为这会破坏向后兼容性。

Is my standard library broken?

没有。 C++ 标准允许这样做;所有 C 标准库名称都保留供语言实现使用(或任何)使用。您可能不会自己定义它们。

Can I do something to avoid this?

您通常无法阻止标准库执行此操作。

您可以通过选择使用独立语言实现从技术上避免大部分问题。但如果你选择那样,你将失去几乎整个标准库。

您可以通过避免自己声明任何全局名称来最大程度地减少名称冲突的可能性,但具有足够唯一名称的单个命名空间除外。类似于:

namespace usr_bitmask::log {

}

1 注意C语言中的"name space"是另外一个概念

2 此外,re-declares 使用名为 header 的 <c...std 命名空间中的名称,如以及在某些情况下添加特定于 C++ 的重载。