标准头文件污染了全局命名空间

pollution of global namespace by standard header files

本文关键字:全局 命名空间 污染 文件 标准      更新时间:2023-10-16

我一次又一次地遇到由于c++标准头文件间接包含C头文件而造成的命名空间污染问题。例如,在我的linux系统上,gcc的(版本5.1.1)<thread>包含usr/include/bits/sched.h,它声明

extern "C" {
  extern int clone(int (*__fn) (void *__arg), void *__child_stack, int __flags, void *__arg, ...) throw();
}

在下面的最小示例中

#include <thread>                              // indirect inclusion of <sched.h>
namespace {
  struct foo
  { virtual foo*clone() const=0; };
  foo*clone(std::unique_ptr<foo> const&);      // function intended
  struct bar : foo
  {
    std::unique_ptr<foo> daughter;
    bar(foo*d) : daughter(d) {}
    foo*clone() const
    { return new bar(::clone(daughter)); }     // to be called here
  };
}

编译器抱怨对::clone()的调用与bits/sched.h的定义不匹配(忽略前面的定义)。(注意,简单地调用clone而不是::clone与成员冲突。)

问题是:

  1. 当试图解决::clone(daughter)函数调用时,gcc是否正确丢弃我的clone()版本?
  2. 以这种方式污染全局命名空间是否符合标准?
  3. 在上面的例子中,我可以解决问题,而不重命名我的clone()函数(或匿名命名空间),但仍然包括<thread> ?
  1. 当试图解决函数调用::clone(女儿)时,gcc是否正确放弃我的clone()版本?

是的,我想是的。

  • 以这种方式污染全局命名空间是否符合标准?
  • 这是有争议的。对于一个纯c++实现,没有,但是没有很多。在实践中,大多数是"c++ on POSIX"或"c++ on Windows"实现,并声明了许多不在c++标准中的名称。

    命名空间污染问题是众所周知的(11196,51749等),没有简单的解决方案。

    问题是大多数c++标准库实现不控制C库,而只是包含平台的本机C头文件,这些头文件会拉入其他名称。

  • 在上面的例子中,我可以不重命名我的clone()函数(或匿名命名空间)但仍然包括解决问题吗?
  • 在您的具体情况下,您可以通过将clone重载放在全局命名空间中来解决名称查找问题,以便查找与<sched.h>中的函数同时找到它,并通过使其静态以再次给予其内部链接。

    1. 是的,因为::clone首先在全局(最外层)命名空间中查找clone,然后在匿名命名空间中查找。
    2. 没有什么可以阻止你(或库提供商)污染任何命名空间(包括全局),但'std'是为标准保留的。
    3. 命名匿名命名空间可能是一个解决办法。

    如果问题#2是"标准是否允许通过包含标准标头来污染全局命名空间,即提供比标准要求更多的符号?"那么我想答案是肯定的,否则强加给标准库供应商的约束可能太严格了。

    在某些情况下,允许实现将定义放入全局命名空间。

    首先,c++库头文件允许包含其他c++头文件:

    N3337 17.6.5.2 Headers [res.on. cn]头]P1:

    一个c++头文件可以包含其他的c++头文件。c++头文件应该提供声明和定义这些都出现在它的摘要中。在其概要中显示的c++头文件应包括其他c++头文件在其他头文件的概要中出现的声明和定义。

    包含的c++头文件可以是C库的包装器。这些c++头文件的名称以c开头,如<cstdlib>。这些头文件允许先将相应的c库定义放在全局命名空间中,然后再注入到std命名空间中。

    17.6.1.2 Headers [Headers] P4:

    除第18条至第30条和附件D规定外,每个报头名称的内容应相同作为C标准库(1.2)或C Unicode中指定的相应头文件名。h的值TR,视情况而定,好像是通过包含。然而,在c++标准库中,声明(除了命名空间std的命名空间作用域(3.3.6)是在C语言中定义为宏的名字未指定这些名称是否首先在全局命名空间范围内声明,然后注入通过显式using-declarations(7.3.3)进入命名空间STD。

    因此,在C标准库中定义的函数可以在全局命名空间中结束。

    clone函数不是C标准库的一部分(它是POSIX标准的一部分),因此c++标准不允许在全局命名空间中出现。