为什么我的登录名为std名称空间?

时间:2012-08-09 22:33:56

标签: c++ namespaces overloading

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

5 个答案:

答案 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 ++中的一个错误

编辑:我声称它是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,则使用命名空间可以避免这种危险。

Proper use of namespace demo

#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