C++ 全局命名空间编译问题

c++ global namespace compile problems

本文关键字:问题 编译 命名空间 全局 C++      更新时间:2023-10-16

我正在使用一个特定的工具链(BlackBerry Playbook NDK附带的工具链)。我认为它基于 GCC 4.4.2。当我尝试使用此工具链编译某些库时,我遇到了我认为是 c++ 全局命名空间的奇怪问题。例如,我会收到如下错误:

'::sprintf' has not been declared

在链接到GNU c ++标准库时,我从未收到这些错误。但是,Playbook NDK 默认使用 Dinkumware C++ libs,我总是必须遍历每个错误,并且通常会添加 C 等效项(在本例中为 stdio.h)。由于我无法控制的原因,我无法链接到GNU c++标准库,它没有表现出任何这些问题。

如果我尝试编译一个更大的项目,我可能会收到数百个这样的错误。是否有不涉及编辑源的解决方法?在上面的例子中,文件通常包含"cstdio"作为标头,但它在"std"命名空间下声明了sprintf。所以我唯一的选择是包含 C 标头或添加 std 命名空间。

实际上

,C++标准要求在标准库中声明的所有符号(包括<c....>标头中的 C 库)都可以在 std:: 命名空间中访问。它并不禁止它们出现在全局命名空间中,并且出于向后兼容性的原因,C++标准库的某些实现(如libstdc++)选择让它们出现。

您有几种可能性:

  • 您可以继续直接包含 C 标头(如 <stdio.h>),但通常不鼓励这样做,因为它们可能C++意识不足,并且可能会产生冲突 (1)
  • 您可以为每个调用添加前缀,尽管这可能不切实际
  • 您可以选择您使用的符号:using std::printf;文件顶部或每个函数中
  • 你可以直截了当地放一个using namespace std;指令。但是你会拉很多东西...

我个人的建议是在调用前面加上前缀,或者挑选最常用的函数并将它们导入全局或当前命名空间中。

在迁移方面,一种直截了当的可能性是实际"包装"标准标头:

// cstdio.hpp
#include <cstdio>
#if defined(_DIRKUMWARE_) // note: you'll have to research the symbol to check
namespace yournamespace {
    using std::printf;
    using std::vptrinf;
} // namespace yournamespace
#endif // defined(_DIRKUMWARE_)

然后让一个脚本将代码库中所有出现的<stdio.h><cstdio>替换为<cstdio.hpp>(显然,除了在这个文件中)。这样,您可以针对标准库的每个实现调整此文件,而不是在包含这些函数的每个文件中重复变通方法。

注(1):C标准没有规定功能是由函数还是宏提供。通常,minmax可能是宏。C++标准规定了功能,这就是为什么使用 C++ 标头通常更安全的原因。

基本上你被困在一个角落里。

根据C++标准:

包括 cstdiostd命名空间中的符号名称导入,也可能在全局命名空间中导入符号名称。

包括 stdio.h 导入全局命名空间中的符号名称,也可能导入 std 命名空间中的符号名称。

因此,如果在包括 cstdio 的特定实现中没有导入全局命名空间中的符号,那么您唯一的选择是对 std 命名空间使用完全限定名称。
这是因为该行为由C++标准明确定义,只是不太为人所知。

如果源包含的几个常见头文件,则可以使用所需的包含内容修改这些文件。

GCC 有一个名为 -include 的命令行选项,允许您在编译源文件的其余部分之前强制源文件包含文件。您可以修改 Makefile 或项目构建脚本的等效项,以为您执行包含。

// c_includes.h
#include <stdio.h>
#include <stdlib.h>
//...
g++ -include c_includes.h ...