标准头文件污染了全局命名空间
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
与成员冲突。)
问题是:
- 当试图解决
::clone(daughter)
函数调用时,gcc是否正确丢弃我的clone()
版本? - 以这种方式污染全局命名空间是否符合标准?
- 在上面的例子中,我可以解决问题,而不重命名我的
clone()
函数(或匿名命名空间),但仍然包括<thread>
?
- 当试图解决函数调用::clone(女儿)时,gcc是否正确放弃我的clone()版本?
是的,我想是的。
- 以这种方式污染全局命名空间是否符合标准?
这是有争议的。对于一个纯c++实现,没有,但是没有很多。在实践中,大多数是"c++ on POSIX"或"c++ on Windows"实现,并声明了许多不在c++标准中的名称。
命名空间污染问题是众所周知的(11196,51749等),没有简单的解决方案。
问题是大多数c++标准库实现不控制C库,而只是包含平台的本机C头文件,这些头文件会拉入其他名称。
- 在上面的例子中,我可以不重命名我的clone()函数(或匿名命名空间)但仍然包括解决问题吗?
在您的具体情况下,您可以通过将clone
重载放在全局命名空间中来解决名称查找问题,以便查找与<sched.h>
中的函数同时找到它,并通过使其静态以再次给予其内部链接。
- 是的,因为
::clone
首先在全局(最外层)命名空间中查找clone
,然后在匿名命名空间中查找。 - 没有什么可以阻止你(或库提供商)污染任何命名空间(包括全局),但'std'是为标准保留的。
- 命名匿名命名空间可能是一个解决办法。
如果问题#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++标准不允许在全局命名空间中出现。
- 在命名空间中使用全局命名空间中的函数
- 将好友从模板导出到全局命名空间
- Catalina C++:使用<cmath>标头产生错误:全局命名空间中没有名为 'signbit' 的成员
- 将以"_[a-z0-9]"开头和"using"的标识符导入全局命名空间是否定义良
- 为什么不允许在全局命名空间处阻止范围?
- 运算符<<依赖于参数的查找不在全局命名空间中查找
- 我可以停止 GCC 将标准库名称包含在全局命名空间中吗?
- "uintmax_t":不是"全局命名空间"的成员
- C++项目中的全局命名空间
- 使用 "::member" 引用全局命名空间有什么用吗?
- 在全局命名空间中重载不依赖于用户定义类型的标准定义类型的运算符是否格式正确?
- 使用全局命名空间限定符和指向数据成员的指针
- InitializeCriticalSectionEx 不是 atlwinverapi.h 中全局命名空间的成员
- C++在命名空间中声明与全局命名空间中的函数同名的函数
- 在vs2010中使用iostream时,出现错误C2039:'exit':不是"全局命名空间"的成员
- 是否可以恢复到"default"全局命名空间?
- C++错误代码全局命名空间
- equal()是否默认包含在全局命名空间中
- 前向声明和全局命名空间声明
- C++在全局命名空间中找不到非标准 C 函数