math.h 宏冲突

math.h macro collisions

math.h 中的宏 DOMAIN 与枚举和可能的其他类型发生冲突。我不知道该怎么做。

#include <algorithm>

enum Type { DOMAIN };


int main(){
    Type t = Type::DOMAIN;
    return 0;

}

使用标志 -std=c++11 编译。尽管此代码的 C99 版本编译得非常好:

#include <algorithm>

enum Type { DOMAIN };


int main(){
    Type t = DOMAIN;
    return 0;

}

我检查了源代码,图书馆是罪魁祸首。算法包括stl_algo.h,其中有ifdef:

#if __cplusplus >= 201103L
#include <random>     // for std::uniform_int_distribution 
#include <functional> // for std::bind
#endif

以下代码在 c++11 编译器上编译良好:

#include <random>
#include <iostream>
int main(){
    std::cout << DOMAIN << std::endl;
    return 0;
}

这是功能还是错误?

编辑* 脏修复:

#ifdef DOMAIN
#undef DOMAIN
#endif

§ 17.6.5.2 [res.on.headers] / N4140 的 1 说:

A C++ header may include other C++ headers. A C++ header shall provide the declarations and definitions that appear in its synopsis. A C++ header shown in its synopsis as including other C++ headers shall provide the declarations and definitions that appear in the synopses of those other headers.

因此,<algorithm>#include <cmath> 是有效的,它会将有问题的宏常量注入到您的命名空间中。

但是请注意,标准 (§ 17.6.4.3.1 [macro.names] / 1) 不允许您的“快速修复”:

A translation unit that includes a standard library header shall not #define or #undef names declared in any standard library header.

您必须为 enum 常量选择一个不同于 DOMAIN 的名称。

这是一个错误(或者 "wart" 如果你想大方一点)。

此答案的所有其余部分仅涉及 GCC 和 Gnu 标准 C 库头文件。 man 页面引用是 linux 系统(但我已将 link 添加到 man7.org)。

DOMAIN 宏来自 math.h 的 System V 支持。 (参见 man matherr.) System V support is normally enabled by defining the _SVID_SOURCE feature-test macro (see man feature_test_macros),但如果定义了 _GNU_SOURCE,它会与大量其他扩展一起启用,或者如果未定义功能测试宏,则默认情况下启用。

如果省略 --std 选项或设置为 gnu##,则

gcc 为 C 程序预定义 _GNU_SOURCE。各种 --std=c## 选项导致 __STRICT_ANSI__ 被定义。因此,使用某些明确的 C 标准编译 C 代码将抑制 System V 扩展。需要这样做是因为 System V 扩展不符合标准,甚至 Posix 也不兼容,因为它们污染了全局命名空间。 (DOMAIN 只是这种污染的一个例子。)

然而,即使指定了 --std=c++##g++ 也会定义 _GNU_SOURCE,因此系统 V 扩展将潜入。(感谢@dyp 提供 link对此 libstdc++ FAQ entry. and this long and inconclusive discussion from 2001 on the GCC mailing list)

一个丑陋的解决方法是自己设置功能,然后取消定义 __USE_SVID:

#include <features.h>
#undef __USE_SVID

#include <random>
#include <iostream>

int main(){   
    std::cout << DOMAIN << std::endl;
    return 0;
}

(Live on coliru)

恕我直言,这应该没有必要。但就是这样。