use*_*370 67 c++ overloading namespaces
在下面的代码中,我定义了一个简单的log函数.在main我尽量不称呼它; 我打电话std::log.然而,我自己log被称为; 我看到"日志!" 在屏幕上.有谁知道为什么?我使用G ++ 4.7和clang ++ 3.2.
#include <iostream>
#include <cmath>
double log(const double x) { std::cout << "log!\n"; return x; }
int main(int argc, char *argv[])
{
std::log(3.14);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
asc*_*ler 57
C++标准17.6.1.2第4段(强调我的):
除第18条至第30条和附件D中所述外,每个标题的内容
cname应与相应标题的内容相同name.h,如C标准库(1.2)或C Unicode TR中所规定,视情况而定包容性.但是,在C++标准库中,声明(在C中定义为宏的名称除外)在命名空间的命名空间范围(3.3.6)内std. 未指定是否首先在全局命名空间范围内声明这些名称,然后std通过显式使用声明将其注入命名空间(7.3.3).
g ++以后一种方式执行,因此可以为C和C++重用一些相同的头文件.因此允许g ++ double log(double)在全局命名空间中声明和定义.
第17.6.4.3.3节第3和第4段:
使用外部链接声明的标准C库中的每个名称都保留给实现,以便
extern "C"在名称空间std和全局名称空间中用作具有链接的名称.使用外部链接声明的标准C库中的每个函数签名都保留给实现,以用作具有两个
extern "C"和extern "C++"链接的函数签名,或者作为全局命名空间中的命名空间范围的名称.
并且在第17.6.4.3节第2段的顶部:
如果程序在保留它的上下文中声明或定义名称,除了本条款明确允许的名称外,其行为是未定义的.
另一方面,您不得::log以任何方式声明或定义.
但是,g ++工具链不会给你任何错误消息太糟糕了.
我希望,会发生什么,std::log只是代表::log.不幸的是,::log只提供float过载,并且您提供double过载,使您的匹配更好.但我仍然没有看到它在重载集中是如何被考虑的.
在libstdc ++上cmath你会看到:
using ::log;
Run Code Online (Sandbox Code Playgroud)
所以它将math.h函数从全局命名空间引入std.不幸的是,您正在提供实现double log(double),因此链接器不会使用math lib中的实现.所以肯定是libstdc ++中的一个错误.
编辑:我声称这是libstdc ++中的一个错误,因为std::log当你明确要求std::版本时,不应该受到C库的干扰.当然,这种覆盖标准库函数的方法是来自C语言的旧"特性".
编辑2:我发现该标准实际上并不禁止将全局命名空间中的名称引入std.所以不是一个错误,只是实现细节的结果.
在C++中,编译器可以自由地在全局命名空间中实现C库并委托给它(这是实现定义的).
17.6.1.2.4除第18条至第30条和附件D中所述外,每个标题cname的内容应与相应的标题名称h的内容相同,如C标准库(1.2)或C中所规定的那样. Unicode TR,视情况而定,就像包含一样.但是,在C++标准库中,声明(除了在C中定义为宏的名称除外)都在命名空间std的命名空间范围(3.3.6)内.这些名称是否首先在全局命名空间范围内声明,然后通过显式使用声明(7.3.3)注入到命名空间std中,这是不确定的.
一般来说,我会避免使用与C标准库之一相同的签名函数.C++标准当然可以让编译器自由选择使用这些签名,这意味着如果你尝试使用相同的签名,你可能会对抗你的编译器.因此,你会得到奇怪的结果.
我会期待链接器错误或警告,我认为值得报告这一点.
[编辑]
哇,忍者.