【问题标题】:math.h macro collisionsmath.h 宏冲突
【发布时间】:2015-10-23 11:03:15
【问题描述】:

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

这是功能还是错误?

EDIT* 脏修复:

#ifdef DOMAIN
#undef DOMAIN
#endif

【问题讨论】:

  • DOMAIN 宏?这是从哪里来的?我在 C++1z 和 C11 草案中都找不到它..
  • 嗯。它似乎与 System V Unix 的 matherr 函数有关,而不是 C 或 C++。设置了__USE_MISC 宏(参见glibc 的features.h);否则 math.h 不会定义 DOMAIN 宏。有可能进入_ISOC11_SOURCE 模式以停用_DEFAULT_SOURCE 以摆脱__USE_MISC。 -- 编辑:...它似乎比这更复杂

标签: c++ c++11 g++ glibc


【解决方案1】:

这是一个错误(如果你想大方一点,它是一个“疣”)。

此答案的其余部分仅指 GCC 和 Gnu 标准 C 库头文件。 man 页面引用指向 linux 系统(但我添加了指向 man7.org 的链接)。

DOMAIN 宏来自math.h 的 System V 支持。 (请参阅man matherr。)通常通过定义_SVID_SOURCE 功能测试宏(请参阅man feature_test_macros)启用 System V 支持,但如果定义了_GNU_SOURCE,则它与大量其他扩展一起启用,或者通过如果没有定义功能测试宏,则默认。

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

但是,即使指定了--std=c++##g++ 也会定义_GNU_SOURCE,因此 System V 扩展将潜入。(感谢@dyp 提供指向此libstdc++ FAQ entry. 和此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)

恕我直言,这应该没有必要。但它就在那里。

【讨论】:

  • @dyp:很酷,我会添加那个链接。此外,我的解决方法不起作用(原因在 FAQ 条目中提到)。
  • @dyp,因为那个帖子是 2001 年的,我的 tl;dr 是“没有人关心修复它。”
  • ...如果这仍然是同样的问题:) 但是,是的,同意。
【解决方案2】:

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

一个 C++ 头文件可能包含其他 C++ 头文件。 C++ 头文件应提供出现在其概要中的声明和定义。在其概要中显示为包含其他 C++ 标头的 C++ 标头应提供 出现在其他标题的概要中的声明和定义。

因此,&lt;algorithm&gt;#include &lt;cmath&gt; 是有效的,它将有问题的宏常量注入你的命名空间。

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

包含标准库头文件的翻译单元不得在任何标准库头文件中声明#define#undef 名称。

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

【讨论】:

  • 好吧,你引用了 C++ 标准,但这个宏是特定于平台的。因此,此处不适用 C++ 要求。
  • 标准不允许cmath 定义DOMAIN,所以我不明白为什么取消定义它应该是个问题。 :) 但请注意 DOMAIN 不是唯一定义的宏。
  • @rici 标准不允许 cmath 定义 DOMAIN - 不太正确。 C++ 标准没有指定或要求定义宏 DOMAIN。但是,操作系统必须遵循除 C++ 之外的其他标准,例如 POSIX。例如。 POSIX 要求void* 与函数和数据指针兼容,C++ 不需要。
  • @MaximEgorushkin:第 1.4 节,第 1 段。 8 “符合标准的实现可能有扩展(包括额外的库函数),只要它们不会改变任何格式良好的程序的行为。” 2.10 第 3 段概述了保留供实施使用的标识符名称; DOMAIN 不是这样的名字,所以它不能是标准库扩展。它可以在非标准库头文件(如 Posix 头文件)中定义,但不能由标准库头文件如&lt;random&gt; 定义。 Posix 符合这个限制;没有冲突。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-26
  • 2021-04-30
  • 1970-01-01
  • 2019-05-16
  • 2011-01-18
  • 1970-01-01
相关资源
最近更新 更多