标准头文件

时间:2015-09-02 13:16:25

标签: c++ namespaces language-lawyer standards-compliance

由于间接包含来自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()时,gcc是否正确丢弃我的::clone(daughter)版本?
  2. 以这种方式对全局命名空间的污染是否符合标准?
  3. 在上面的示例中,我是否可以在不重命名clone()函数(或匿名命名空间)但仍包含<thread>的情况下解决问题?

3 个答案:

答案 0 :(得分:4)

  
      
  1. 在尝试解析函数调用:: clone(daughter)时,gcc是否正确丢弃我的clone()版本?
  2.   

是的,我想是的。

  
      
  1. 以这种方式对全局命名空间的污染是否符合标准?
  2.   

这是有争议的。对于纯C ++实现,没有,但是没有很多。在实践中,大多数是&#34; C ++在POSIX&#34;或&#34; C ++ on Windows&#34;实现并声明许多不在C ++标准中的名称。

命名空间污染问题众所周知(1119651749和其他),没有简单的解决方案。

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

  
      
  1. 在上面的示例中,我是否可以在不重命名我的clone()函数(或匿名命名空间)的情况下解决问题,但仍然包括?
  2.   

在您的特定情况下,您可以通过将clone重载放在全局命名空间中来解决名称查找问题,以便查找在<sched.h>中的函数同时找到它并将其设置为静态再次给它内部联系。

答案 1 :(得分:1)

  1. 是的,因为::clone首先在全局(最外层)命名空间中查找clone,然后在匿名命名空间中查找。{/ li>
  2. 没有什么可以阻止您(或图书馆提供者)污染任何名称空间(包括全局),但是为标准保留的“标准”。
  3. 命名匿名命名空间可能是一条出路。
  4. 如果问题#2是“标准是否允许通过包括标准标题来污染全局命名空间,即提供比标准要求更多的符号?”。然后我猜答案是肯定的,否则对标准库供应商施加的限制可能过于严格。

答案 2 :(得分:0)

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

首先,允许C ++库头包含其他C ++头:

N3337 17.6.5.2标题[res.on.headers] P1:

  

C ++标头可能包含其他C ++标头。 C ++头应提供声明和定义   出现在其概要中。在其概要中显示的C ++头文件应包含其他C ++头文件   显示在其他标题的概要中的声明和定义。

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

17.6.1.2标题[标题] P4:

  

除第18条至第30条和附件D中所述外,每个标题cname的内容应相同   与C标准库(1.2)或C Unicode中指定的相应头名称h相同   TR,视情况而定,如同包含一样。但是,在C ++标准库中,声明(除了   在C)中定义为宏的名称在命名空间std的命名空间范围(3.3.6)内。它是   未指定是否首先在全局命名空间范围内声明这些名称,然后注入这些名称   通过显式使用声明(7.3.3)进入命名空间std。

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

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