如何解决C++命名空间和全局函数之间的名称冲突?

12
如果我在某处定义了一个命名空间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'

@cj:你可能会想看一下 这个答案 - sbi
1
通过C标准头文件<c****>导入的C标识符不应该位于std命名空间中吗?这是v4.1实现的错误(在g++ 4.4中,此代码可以正常工作),还是我记错了这件事? - Matteo Italia
1
@Matteo Italia:这些标识符也必须在全局命名空间中可访问。请参见 http://stackoverflow.com/questions/1524139/ansi-c-functions-namespace-in-iso-c/1524387#1524387 。 - cj.
2
据我理解,根据这些标准引用,只有在包含<name.h> C头文件时,这些标识符才必须放置在全局命名空间中,而包含<cname>则应该将它们放置在std命名空间中。 - Matteo Italia
1
我发现这个 bug 非常令人恼火,我可以修补 /usr/include/bits/mathcalls.h,但是文件 <cmath> 期望例如 log 在全局命名空间中(参见 gcc 的 stdlib <cmath> 的第 356 行,它读取 using ::log;)。我想知道是否可以通过操作所有定义的宏来纠正所有这些行为。顺便说一下,@MatteoItalia,在 gcc 4.7 中也发现了这个问题,所以如果在 4.4 中修复了该 bug,则该 bug 又回来了。 - alfC
显示剩余3条评论
4个回答

13

我建议:

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

一般情况下,我不会写 using namespace foo;,因为如果你不打算使用它,它在 foo 命名空间中是没有意义的,而且它会污染全局命名空间。

参见相关问题:
如何正确使用 C++ 中的命名空间?


9
尽管它对你没有帮助,但GCC 4.1.2的错误是不正确的。在log::Log中的log只能引用类或命名空间名称。
如果你的代码还需要在GCC 4.1.2上编译,那么有两个选项:
  1. 使用完全限定名称foo::log::Log
  2. 使用命名空间别名:

    namespace log1 = foo::log;
    log1::Log logger;

1
+1 对使用别名的想法表示赞同。你确定错误是不正确的吗?我猜你的意思是作用域运算符解决了歧义,因为它不能应用于函数名?对于这种特殊情况,我不确定标准是怎么说的。但是关于命名空间定义,请参阅第7.3.1节:“原始命名空间定义中的标识符不得在原始命名空间定义所在的声明区域中先前定义过。” - cj.
1
我确信这个错误是不正确的。我不得不在标准中查找,根据语法规则,在作用域运算符之前只能出现类或命名空间名称。你的“log”命名空间的声明并不是问题,因为它没有被定义在与“log”函数相同的命名空间中。(命名空间在“foo”命名空间内,函数在全局命名空间中) - Bart van Ingen Schenau

1

cmath 之所以使用 ::log,是为了从全局作用域获取它,并且无法确定函数和命名空间之间的区别。

命名空间可以使代码保持独立,以防止函数签名的 混淆污染

这里有一个完整且详细说明了 适当 命名空间 使用的示例:

#include <iostream>
#include <cmath>  // Uses ::log, which would be the log() here if it were not in a namespace, see https://dev59.com/Kmct5IYBdhLWcg3wsPd5

// 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

0
在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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接