由于间接包含来自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
与成员发生冲突。)
所以,问题是:
clone()
时,gcc是否正确丢弃我的::clone(daughter)
版本?clone()
函数(或匿名命名空间)但仍包含<thread>
的情况下解决问题?答案 0 :(得分:4)
- 在尝试解析函数调用:: clone(daughter)时,gcc是否正确丢弃我的clone()版本?
醇>
是的,我想是的。
- 以这种方式对全局命名空间的污染是否符合标准?
醇>
这是有争议的。对于纯C ++实现,没有,但是没有很多。在实践中,大多数是&#34; C ++在POSIX&#34;或&#34; C ++ on Windows&#34;实现并声明许多不在C ++标准中的名称。
命名空间污染问题众所周知(11196,51749和其他),没有简单的解决方案。
问题在于,大多数C ++标准库实现都不能控制C库,只是包含平台的本机C头,这些C头引入了其他名称。
- 在上面的示例中,我是否可以在不重命名我的clone()函数(或匿名命名空间)的情况下解决问题,但仍然包括?
醇>
在您的特定情况下,您可以通过将clone
重载放在全局命名空间中来解决名称查找问题,以便查找在<sched.h>
中的函数同时找到它并将其设置为静态再次给它内部联系。
答案 1 :(得分:1)
::clone
首先在全局(最外层)命名空间中查找clone
,然后在匿名命名空间中查找。{/ li>
如果问题#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 ++标准出现在全局命名空间中。