为什么我的日志在std命名空间中?
在下面的代码中,我定义了一个简单的log
function。 main
我尽量不要打电话。 我打电话std::log
。 不过,我自己的log
被称为; 我看到“login” 在屏幕上。 有谁知道为什么? 我使用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; }
C ++标准17.6.1.2第4段(强调我的):
除第18条至第30条和附录D中所述外,每个标题
cname
的内容应与C标准库(1.2)或C Unicode TR中指定的相应标题name.h
的内容相同,好像通过包含。 然而,在C ++标准库中,声明(C中定义为macros的名称除外)位于名称空间std
的名称空间范围(3.3.6)内。 没有指定这些名称是否在全局命名空间范围内首先声明,然后通过显式使用声明 (7.3.3)注入命名空间std
。
g ++是用后一种方式做的,所以一些相同的头文件可以被C和C ++重用。 所以g ++被允许在全局命名空间中声明和定义double log(double)
。
第17.6.4.3.3段第3和4段:
使用外部链接声明的标准C库中的每个名称都保留给实现,以用作名称空间
std
和全局名称空间中具有extern "C"
链接的名称。从外部链接声明的标准C库中的每个函数签名都保留给实现,以用作具有
extern "C"
和extern "C++"
链接的函数签名,或者作为全局名称空间中的名称空间作用域的名称。
在第17.6.4.3段第2段的顶部:
如果一个程序在保留的上下文中声明或者定义了一个名字,除了这个子句明确允许的以外,它的行为是不确定的。
另一方面,您可能不会以任何方式声明或定义::log
。
但是,g ++工具链不会给你任何错误信息,这太糟糕了。
我期望的是, std::log
简单地委托给::log
。 不幸的是, ::log
只提供了一个float
重载,并且你提供了一个double
重载,使你更好的匹配。 但我仍然没有看到它是如何在超负荷设置考虑。
在libstdc ++的cmath
你会看到这个:
using ::log;
所以它将math.h函数从全局名字空间引入到std
。 不幸的是,你提供了double log(double)
,所以链接器将不会使用math库中的一个。 所以绝对是libstdc ++中的一个bug 。
编辑:我声称这是一个libstdc ++中的错误,因为当明确要求std::
versions时, std::log
不应该受到C库的干扰。 当然,这种覆盖标准库函数的方式是C语言的一个旧“function”。
编辑2:我发现这个标准实际上并不禁止将全局名称空间的名称带入std
。 所以不是一个错误,只是实现细节的一个后果。
在C ++中,编译器可以自由地在全局名称空间中实现C库并委托给它(这是实现定义的)。
17.6.1.2.4除第18条至第30条和附录D中所述外,每个标题cname的内容应与C标准库(1.2)或C标准库中规定的相应标题name.h相同Unicode TR,视情况而定,好像包含在内。 然而,在C ++标准库中,声明(除了在C中被定义为macros的名称)在命名空间std的命名空间范围(3.3.6)内。 没有说明这些名称是否是在全局命名空间范围内首先声明的,然后通过显式使用声明 (7.3.3) 注入命名空间std中 。
一般来说,我会避免使用与C标准库之一相同的签名function。 C ++标准肯定会给编译器自由地使用这些签名(如果它select的话),这意味着如果您尝试使用相同的签名,则可能会对抗您的编译器。 因此,你会得到奇怪的结果。
我希望链接器错误或警告,但我认为这可能是值得报告的。
[编辑]
哇,忍者
因为你已经在全局命名空间中重写了它。 例如,如果您不想继续使用像Nim这样更安全,更干净的语言,那么使用名称空间可以避免这种危险。
正确使用命名空间演示 :
#include <iostream> #include <cmath> // Uses ::log, which would be the log() here if it were not in a namespace, see http://stackoverflow.com/questions/11892976/why-is-my-log-in-the-std-namespace // Silently overrides std::log //double log(double d) { return 420; } namespace uniquename { using namespace std; // So we don't have to waste space on std:: when not needed. double log(double d) { return 42; } int main() { cout << "Our log: " << log(4.2) << endl; cout << "Standard log: " << std::log(4.2); return 0; } } // Global wrapper for our contained code. int main() { return uniquename::main(); }
输出:
Our log: 42 Standard log: 1.43508