为什么我的日志在std命名空间中?

在下面的代码中,我定义了一个简单的logfunction。 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