Why is std::min failing when windows.h is included?

#include <algorithm>
#include <Windows.h>


int main()
{
int k = std::min(3, 4);
return 0;
}

What is windows doing if I include Windows.h? I can't use std::min in visual studio 2005. The error message is:

error C2589: '(' : illegal token on right side of '::'
error C2059: syntax error : '::'
70420 次浏览

windows.h头文件(或者更准确地说,它依次包含的 windef.h)具有干扰 minmax的宏。

你应该在包括它之前 #define NOMINMAX


事实上,即使存在 没有冲突,您也应该这样做,因为宏的幼稚定义说明了为什么类函数宏是一个坏主意:

#define max(a,b) ((a)>(b)?(a):(b))

如果使用以下方法调用该宏,例如:

int x = 5, y = 10;
int c = max(x++, y--);

那么 y就不会达到你所期望的结果。例如,它会扩展到:

int c = ((x++)>(y--)?(x++):(y--));

这个表达式(除非出现未定义的行为,否则情况会更糟糕)会将 y减少两次,这不是您可能期望的结果。

现在我基本上只在条件编译中使用宏,其他两个主要的旧用例(符号常量和类函数宏)用更现代的语言特性(实际枚举类型和内联函数建议)处理得更好。

我假设 windows.h 确实将 min 定义为宏,例如 like

#define min(a,b)  ((a < b) ? a : b)

这就解释了错误消息。

试试这样:

#define NOMINMAX
#include <windows.h>

默认情况下,windows.h 将 minmax定义为宏。当它们被展开时,尝试使用 std::min的代码(例如)最终会看起来像这样:

int k = std::(x) < (y) ? (x) : (y);

错误消息告诉您不允许使用 std::(x)

#define NOMINMAX

是抑制 max 和 min 的宏定义的技巧

Http://support.microsoft.com/kb/143208

正如其他人所提到的,这些错误是由于在 Windows 头中定义的 min/max 宏造成的。有三种方法可以解除它们。

1)在包含头之前的 #define NOMINMAX,这通常是定义宏以影响下列头的一种不好的技术;

2)在编译器命令行/IDE 中定义 NOMINMAX。这个决定的不好之处在于,如果你想发布你的源代码,你需要警告用户做同样的事情;

3)在使用代码中的宏之前简单地取消它们的定义

#undef min
#undef max

这可能是最便携和灵活的解决方案。

不需要定义任何东西,只需使用以下语法绕过宏:

(std::min)(a, b); // added parentheses around function name
(std::max)(a, b);

在我的情况下,项目没有包括 windows.hwindef.h明确。它在使用 Boost。因此,我通过转到项目 Properties -> C/C++ -> Preprocessor并在 Preprocessor Definitions(VS 2013,VS 2015)中附加 NOMINMAX来解决这个问题。

我的首选解决方案是将类型显式化,如下所示:

auto k = std::min<int>(3, 4);

这也阻止了预处理器与 min匹配,并且可以说比圆括号解决方案更具可读性。NOMINMAX解决方案的一个问题是,当您的代码在下一个项目中被重用时,您(或其他人)必须重新开始询问为什么这个“工作”代码不再编译了。

然而要回答实际问题 为什么会失败?

Windows.h文件包括定义以下宏的 windef.h:

#ifndef NOMINMAX
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
#endif

预处理器实际上是通过在代码中查找包含开括号的字符序列(在本例中为 min()来扩展宏。然后它会对你的代码进行一个基本的搜索/替换,所以在预处理步骤之后,你的代码变成:

int k = std::((3)<(4)?(3):(4));

编译器步骤,只有这个修改过的代码继续,然后失败时,尝试编译 std::(给您您的错误消息。

现在,您还可以看到本页提出的各种修复/解决方案是如何工作的:

  1. 在包含 windef.h之前定义 NOMINMAX意味着不会定义这些宏,因此编译器可以编译 std::min
  2. 将括号放在 std::min周围意味着预处理器不再看到序列 min(,因此不再管它。
  3. 输入模板参数意味着 min<也与宏不匹配。

对于包括 windows.h 在内的用户,将以下内容放在生效的标题中:

#include windows headers ...


pragma push_macro("min")
pragma push_macro("max")
#undef min
#undef max


#include headers expecting std::min/std::max ...


...


pragma pop_macro("min")
pragma pop_macro("max")

在源文件中只有 # undef min 和 max。

#include windows headers ...


#undef min
#undef max


#include headers expecting std::min/std::max ...

为了解决这个问题,我只需要创建一个名为 fix_minmax.h 没有 include Guard 的头文件

#ifdef max
#undef max
#endif


#ifdef min
#undef min
#endif


#ifdef MAX
#undef MAX
#endif
#define MAX max


#ifdef MIN
#undef MIN
#endif
#define MIN min


#include <algorithm>
using std::max;
using std::min;

基本用法是这样的。

// Annoying third party header with min/max macros
#include "microsoft-mega-api.h"
#include "fix_minmax.h"

这种方法的优点是它可以处理所包含的每一种文件或代码的一部分。这也节省了处理依赖于 min/max宏的代码或库的时间