在下面的代码中,我定义了一个简单的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;
}
答案 0 :(得分:57)
C ++标准17.6.1.2第4段(强调我的):
除第18条至第30条和附件D中所述外,每个标题
cname
的内容应与C标准库(1.2)中指定的相应标题name.h
的内容相同。 )或C Unicode TR,视情况而定,如同包含一样。但是,在C ++标准库中,声明(除了在C中定义为宏的名称除外)在命名空间std
的命名空间范围(3.3.6)内。 未指定这些名称是否首先在全局命名空间范围内声明,然后通过显式 using-declarations (7.3.3)注入命名空间std
。
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 ++工具链并没有给你任何错误信息,这太糟糕了。
答案 1 :(得分:8)
我希望,std::log
只需委托::log
即可。不幸的是,::log
仅提供float
重载,并且您提供double
重载,使您的匹配更好。但是我仍然没有看到在重载集中如何考虑它。
答案 2 :(得分:8)
在libstdc ++的cmath
上你会看到:
using ::log;
因此它将math.h函数从全局名称空间引入std
。不幸的是,您正在为double log(double)
提供实现,因此链接器不会使用math lib中的实现。 这绝对是libstdc ++中的一个错误。
std::log
在您明确要求std::
版本时不应受到对C库的干扰。当然,这种覆盖标准库函数的方法是来自C语言的旧“特性”。
编辑2:我发现标准实际上并不禁止将全局命名空间中的名称引入std
。所以不是一个错误,只是实现细节的结果。
答案 3 :(得分:6)
在C ++中,编译器可以自由地在全局命名空间中实现C库并委托给它(这是实现定义的)。
17.6.1.2.4除第18条至第30条和附件D所述外,每个标题cname的内容应相同 与C语言库(1.2)或C Unicode中指定的相应头名称h相同 TR,视情况而定,如同包含一样。但是,在C ++标准库中,声明(除了 在C)中定义为宏的名称在命名空间std的命名空间范围(3.3.6)内。 是的 未指定这些名称是否首先在全局命名空间范围内声明,然后注入 通过显式使用声明进入命名空间std (7.3.3)。
通常,我会避免使用与C标准库之一相同的签名来创建函数。 C ++标准当然可以让编译器自由选择使用这些签名,这意味着如果你尝试使用相同的签名,你可能正在与你的编译器作斗争。因此,你会得到奇怪的结果。
我希望链接器出现错误或警告,我认为值得报告此事。
[编辑]
哇,忍者。答案 4 :(得分:0)
因为您已在全局命名空间中覆盖它。如果您不希望转向更安全,更清晰的语言,例如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