<c____>头文件中声明的C函数是否保证在全局命名空间和std命名空间中都存在?

17

这是我一直想知道但从未确定的事情,这只是纯粹出于好奇,不是真正的问题。

据我所知,当你像#include <cstdlib>这样做时,除了宏之外,所有内容都在std::命名空间中声明。 我见过的每个实现都是通过以下方式实现的:

#include <stdlib.h>
namespace std {
    using ::abort;
    // etc....
}

当然,这样做的效果就是让事物既在全局命名空间中,也在std中。这种行为是否得到了保证?或者说,实现可能会将这些东西放在std中,而不是全局命名空间中吗?我能想到的唯一方法是让你的libstdc++自己实现每个c函数,并直接将它们放入std中,而不仅仅是包含现有的libc头文件(因为没有从命名空间中删除内容的机制)。这当然需要付出很多努力,但收益却微乎其微。

我的问题的要点是,以下程序是否严格符合标准,并保证能够正常工作?

#include <cstdio>
int main() {
    ::printf("hello world\n");
}

编辑: 我找到的最接近的是这句话 (17.4.1.2p4):

除了第18至27条款中注明的内容外,每个头文件cname的内容应与相应的头文件name.h的内容相同,如同被包含在其中一样,具体规定见ISO / IEC 9899:1990编程语言C(第7条)或ISO / IEC:1990编程语言—C修正案1:C完整性(第7条),视情况而定。然而,在C++标准库中,声明和定义(除了在C中定义为宏的名称)位于std名称空间的命名空间范围内(3.3.5)。

说实话,我可能会对此有两种解释。 "每个头文件cname的内容应与相应的头文件name.h的内容相同,如同被包含在其中一样,具体规定见ISO / IEC 9899:1990编程语言C"告诉我它们可能需要在全局名称空间中,但是 "然而,在C++标准库中,声明和定义(除了在C中定义为宏的名称)位于std名称空间的命名空间范围内(3.3.5)"则表示它们在std中(但不指定任何其他作用域)。


据我所知,随GCC一起提供的GNU头文件在namespace std声明内包含了旧的头文件。 - greyfade
至少STLPort4的某些版本确实只将函数引入std命名空间,而不是全局命名空间。 - Mark B
1
还可以看一下Jonathan Wakely在Red Hat博客上的文章为什么<cstdlib>比你想象的更复杂。Wakely是GCC的C++标准库维护者之一。我认为<math.h>与<cmath>之间的区别是一个更有趣的案例研究。 - jww
3个回答

10
以下是一份来自MSVC团队成员Stephan T. Lavavej的漂亮情况总结(包括现实情况与标准规定之间的区别):http://blogs.msdn.com/vcblog/archive/2008/08/28/the-mallocator.aspx#8904359
引用如下: - 同样,应该使用、和std::size_t等。 - 我曾经非常注意这个问题。C++98有一个辉煌的梦想,即将在namespace std中声明所有内容,将包括,然后使用声明将所有内容拖入全局命名空间。(这是D.5 [depr.c.headers])。) - 但很多实现者忽略了这一点(其中一些对C标准库头文件几乎没有控制权)。因此,C++0x已经改变以符合现实。根据N2723工作文件,http://open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2723.pdf,现在保证声明了namespace std中的所有内容,并且可能或可能不会在全局命名空间中声明内容。相反:它保证在全局命名空间中声明了所有内容,并且可能或可能不会在namespace std中声明内容。 - 无论在现实中还是在C++0x中,包括都不能确保所有内容都被声明在全局命名空间中。这就是为什么我不再关注的原因。 - 这是库问题456,http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#456。 - (C++0x仍然将C标准库的头文件标记为废弃,这真是好笑。)
我从来不喜欢头文件,发现自己总是使用。现在我感觉我可以放心地不用担心我的C++“纯洁性”方面的缺乏了。

2
不同的人有不同的喜好。我个人认为“c”比“.h”少烦人多了 :) - Billy ONeal
1
很有趣,感谢您指出这一点。我一直喜欢<cfoo>的外观,但是没有看到写std::size_t相比于size_t有什么优势,所以一直使用后者,并且对将来某一天可能无法正常工作感到有些紧张。我感觉在不久的将来需要重新调整我的#include风格... - John Marshall
我个人不太喜欢<cfoo>头文件,我一直使用的是<foo.h>……针对<math.h>abslog以及缺少的重载,你会怎么做呢?我曾经见过这些缺失的重载导致编译出错。 - jww
1
@jww 你很幸运能够得到编译错误。我曾被gcc的怪异之处所困扰,例如std::sqrt(long double)存在于<cmath>中,而::sqrt(long double)则不存在,但是std::sqrt(double)::sqrt(double)都存在——因此如果你写错了sqrt(myLongDoubleVar),你将会调用::sqrt(double)而没有任何警告,从而失去精度,即使使用了-Wall -Wextra选项。同时,如果你包含了<math.h>,所有的重载都存在于全局命名空间中。 - Ruslan
@Ruslan - 另外请��阅Jonathan Wakely的文章[为什么<cstdlib>比你想象中更复杂](https://developers.redhat.com/blog/2016/02/29/why-cstdlib-is-more-complicated-than-you-might-think/),该文章来自Red Hat博客。 Wakely是GCC的C++标准库维护者之一。 - jww

4
目前来看是不行的。事实上,尽管这段代码可以在我所知道的每个编译器上运行,但它实际上根本不应该工作 - #include其中一个c*头文件只应该让您访问名称空间std内的名称。
由于实现这一点非常痛苦(要想正确地完成,基本上需要在正确的命名空间中将整个C库复制为C++库),在C ++ 0x中它们已经稍微改变了要求 - 您的代码现在被允许工作,尽管(如果记忆服务)仍然没有必要工作。

但是库头文件可以包含其他库头文件,这似乎允许全局范围内的名称,不是吗? - Ben Voigt
1
@Ben:我不这么认为。具体措辞是(§17.4.4/1):“C++头文件可以包含其他C++头文件。”然而根据§D.5/1的规定:“为了与标准C库兼容,C++标准库提供了18个C头文件……”。由于它们被明确称为“C头文件”,而不是“C++头文件”,我认为它们不在该许可范围内(但我可以理解为什么会有不同意见)。 - Jerry Coffin

0
我无法对标准发表意见,因为我没有阅读过它们,但是可以想象一个不是建立在C环境之上的C++环境,或者C环境只是基于底层C++ API的兼容性层。在这种情况下,这些保证可能无法实现。如果这样的实现被禁止成为符合规范的实现,我会感到惊讶。

足够准确,但可能需要确保其效果相同(基本上需要在命名空间块之后加入适当的using std :: xxxx;声明将它们手动放入全局命名空间中)。 - Evan Teran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接