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

pollution of global namespace by standard header files

由于从 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 首先在全局(最外层)命名空间中查找 clone,然后在匿名命名空间中查找。
  2. 没有什么可以阻止您(或库提供者)污染任何命名空间(包括全局),但 'std' 为标准保留。
  3. 命名匿名命名空间可能是一个出路。

如果问题 #2 是 "Does the standard allow the pollution of global namespace by including a standard header, i.e. making available more symbols than the standard require?"。那我猜答案是肯定的,否则对标准库供应商的约束可能太严格了。

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

首先,允许一个 C++ 库 header 包含其他 C++ headers:

N3337 17.6.5.2 Headers [res.on.headers] P1:

A C++ header may include other C++ headers. A C++ header shall provide the declarations and definitions that appear in its synopsis. A C++ header shown in its synopsis as including other C++ headers shall provide the declarations and definitions that appear in the synopses of those other headers.

包含的 C++ header 可以是 C 库的包装器。这些 C++ header 的名称以 c 开头,如 <cstdlib>。 允许这些header先把对应的C-library定义放到全局命名空间,然后注入到std命名空间。

17.6.1.2 Headers [headers] P4:

Except as noted in Clauses 18 through 30 and Annex D, the contents of each header cname shall be the same as that of the corresponding header name.h, as specified in the C standard library (1.2) or the C Unicode TR, as appropriate, as if by inclusion. In the C++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope (3.3.6) of the namespace std. It is unspecified whether these names are first declared within the global namespace scope and are then injected into namespace std by explicit using-declarations (7.3.3).

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

但是,

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

  1. Is gcc correct to discard my version of clone() when trying to resolve the function call ::clone(daughter)?

是的,我也这么认为。

  1. Is the pollution of the global namespace in this way standard compliant?

这是有争议的。对于纯 C++ 实现,没有,但数量不多。实际上,大多数是 "C++ on POSIX" 或 "C++ on Windows" 实现,并声明了许多不在 C++ 标准中的名称。

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

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

  1. In the above example, can I resolve the problem without renaming my clone() functions (or the anonymous namespace) but still including ?

在您的特定情况下,您可以通过将 clone 重载放在全局命名空间中来解决名称查找问题,这样查找可以与 <sched.h> 中的函数同时找到它,并使它static 再次给它内部链接。