【问题标题】:How to resolve a name collision between a C++ namespace and a global function?如何解决 C++ 命名空间和全局函数之间的名称冲突?
【发布时间】:2011-04-21 18:02:05
【问题描述】:

如果我在某处定义命名空间 log 并使其在全局范围内可访问,这将与标准 cmath 标头中的 double log(double) 冲突。实际上,大多数编译器似乎都支持它——大多数版本的 SunCC、MSVC、GCC——但 GCC 4.1.2 没有。

不幸的是,似乎没有办法解决歧义,因为using 声明对于命名空间标识符是不合法的。即使包含cmath,您知道我可以在全局命名空间中写入log::Log 的任何方法吗?

谢谢。

编辑:有人知道 C++03 标准对此有何规定吗?我会认为范围运算符足以消除下面代码示例中 log 的使用的歧义。

#include <cmath>

namespace foo
{

namespace log
{

struct Log { };

} // namespace log

} // namespace foo


using namespace foo;

int main()
{
    log::Log x;

    return 0;
}

// g++ (GCC) 4.1.2 20070115 (SUSE Linux)

// log.cpp: In function `int main()':
// log.cpp:20: error: reference to `log' is ambiguous
// /usr/include/bits/mathcalls.h:110: error: candidates are: double log(double)
//     log.cpp:7: error:                 namespace foo::log { }
// log.cpp:20: error: expected `;' before `x'

【问题讨论】:

  • 为什么不直接写成foo::log::Log来避免歧义呢?
  • 通过&lt;c****&gt; 版本的C 标准头文件导入的C 标识符不应该位于std 命名空间中吗?是 v4.1 的实现错误(在 g++ 4.4 中此代码工作正常)还是我记得这件事错了?
  • @Matteo Italia:这些标识符必须在全局命名空间中也可以访问。见stackoverflow.com/questions/1524139/…
  • @cj:据我从这些标准引用中了解到,就标准而言,只有当您包含 &lt;name.h&gt; C 标头时,这些标识符才必须放在全局命名空间中,而包括&lt;cname&gt; 应该只将它们放在std 命名空间中。
  • 我发现这个 bug 很烦人,我可以修补 /usr/include/bits/mathcalls.h 但是文件 &lt;cmath&gt; 期望 log 例如在全局命名空间中(参见 gcc 的 stdlib @ 中的第 356 行987654339@,它读取using ::log;。我想知道是否可以使用所有定义的宏来纠正所有这些行为。顺便说一下,@MatteoItalia,我在 gcc 4.7 中也发现了这个,所以如果它在 4.4 中得到修复错误又回来了。

标签: c++ namespaces name-conflict


【解决方案1】:

在 c++0x 或 c++11 中,以下应该可以工作,使用 -std=c++0x 或 -std=c++11 编译:

#include <iostream>
#include <math.h>

namespace ___ {
namespace log {

void init() {
    std::cout << "log::init()" << std::endl;
}

} // namespace log
} // namespace ___

using namespace ___;

int main() {
    log::init();
    std::cout << ::log(3.2) << std::endl;
    return 0;
}

【讨论】:

    【解决方案2】:

    cmath 出于某种原因使用 ::log 从全局范围获取它,并且无法在函数和命名空间之间做出决定。

    命名空间保留代码以防止 confusionpollution 函数签名。

    Hereproper namespace 用法的完整且有文档的演示:

    #include <iostream>
    #include <cmath>  // Uses ::log, which would be the log() here if it were not in a namespace, see https://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
    

    【讨论】:

      【解决方案3】:

      虽然它对您没有帮助,但来自 GCC 4.1.2 的错误是不正确的。 log::Log 中的 log 只能引用类或命名空间名称。

      如果您的代码还需要使用 GCC 4.1.2 进行编译,那么有两种选择:

      1. 使用完全限定名称foo::log::Log
      2. 使用命名空间别名:
      
          namespace log1 = foo::log;
          log1::Log logger;
      

      【讨论】:

      • +1 表示使用别名的想法。你确定错误不正确吗?我认为您的意思是范围运算符解决了歧义,因为它不能应用于函数名称?我不确定标准对这种特殊情况有何规定。但关于命名空间定义,请参阅第 7.3.1 节:“原始命名空间定义中的标识符之前不应在原始命名空间定义出现的声明区域中定义。”
      • 我确定错误是不正确的。我不得不在标准中查找它,并且根据语法,在范围运算符之前只能出现一个类或命名空间名称。 log 命名空间的声明不是问题,因为它没有在与 log 函数相同的命名空间中定义。 (命名空间在foo命名空间,函数在全局命名空间中)
      【解决方案4】:

      我建议:

      foo::log::Log x; // Your logging class
      ::log(0.0); // Log function
      

      通常我不会写using namespace foo;,因为如果你不打算使用它,将它放在foo 命名空间中是没有意义的,它会污染全局命名空间。

      查看这个相关问题:
      How do you properly use namespaces in C++?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-11
        • 1970-01-01
        • 1970-01-01
        • 2011-01-31
        • 2022-01-06
        • 1970-01-01
        • 1970-01-01
        • 2016-02-20
        相关资源
        最近更新 更多