2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C++】Windows.hのmin/maxは静かにソースコードを壊す:原因と対策法

2
Last updated at Posted at 2026-02-18

Windows APIを使ったC++開発では、原因不明のコンパイルエラーや、テンプレートが突然壊れる不可解な現象に遭遇することがあります。

その場合、<Windows.h>が定義するマクロが静かに悪さをしていることがあります。

本記事では、その代表例である min/max マクロがなぜ危険なのか、そしてその対策法について紹介します。

前提知識

  • C++のマクロについて知っている
  • Windowsヘッダーを使ったことがある

何が問題なのか

Windows APIを利用するために<Windows.h>をインクルードすると、ヘッダ内部で大量のマクロが自動的に定義されます。

その中でも特に問題になりやすいのが、次の 2 つのマクロです。

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

これらはWindowsが古くから提供している便利マクロですが、C++の標準ライブラリが提供するstd::min/std::maxと名前が衝突します。

マクロはプリプロセッサによる強制置換であるため、もし衝突してしまった場合、std::min(x, y)のようなコードが マクロ展開されてしまい、意図しない形に書き換えられてしまいます。

外部ライブラリなどを使用してデスクトップアプリ開発をしている場合、そのライブラリがstd::min()std::maxを使用していたために、意図しない形に展開されてしまって謎のコンパイルエラーが出るということがよくあります。

サンプルコード

最も単純なエラーの例

main.cpp
//--------------------------------------------------------------------------
//! @file	main.cpp
//! @brief	Windows.hの問題点を提示するサンプルコード
//! @author つきの
//--------------------------------------------------------------------------
#include <Windows.h>
#include <algorithm>
// エントリポイント
int main()
{
	// 適当に値を用意する
	int a = 1;
	int b = 2;

	// Windows.hのmin()マクロが邪魔して、std::min()が呼び出せない
	int c = std::min(a, b);
	// プログラムの終了
	return 0;
}
error
重大度レベル	コード	説明	プロジェクト	ファイル	行	抑制状態	詳細
エラー (アクティブ)	E0040	識別子が必要です	NOMINMAXTest	C:\Users\maihe\Desktop\NOMINMAXTest\NOMINMAXTest\main.cpp	16		
重大度レベル	コード	説明	プロジェクト	ファイル	行	抑制状態	詳細
エラー	C2059	構文エラー: ')'	NOMINMAXTest	C:\Users\maihe\Desktop\NOMINMAXTest\NOMINMAXTest\main.cpp	16		
重大度レベル	コード	説明	プロジェクト	ファイル	行	抑制状態	詳細
エラー	C2589	'(': スコープ解決演算子 (::) の右側にあるトークンは使えません。	NOMINMAXTest	C:\Users\maihe\Desktop\NOMINMAXTest\NOMINMAXTest\main.cpp	16		

具体的なエラーの理由

上記のコードがエラーになる理由は、
std::min(a, b)の中のminが、Windows.hのマクロによって強制的に書き換えられてしまうためです。

C++コンパイルは**プリプロセッサ(マクロ展開)**→ **コンパイラ(文法解析)**の順で行われます。

今回のmin(a,b)マクロはプリプロセッサによって

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

と展開されます。

つまり

main.cpp
	int c = std::min(a, b);

と書いていましたが、プリプロセッサ

  int c = std::((a) < (b) ? (a) : (b));

と置き換えたことになります。

こうなるとコンパイラが文法解釈を行う頃には、我々が書いたコードの意図はかき消されてしまっているというわけです。

回避方法

最も確実な対策は、Windows.hmin/maxマクロを定義しないようにすることです。

そのためにWindows APIは公式にNOMINMAXマクロを用意しています。

NOMINMAX を使った安全なコード例

main.cpp
//--------------------------------------------------------------------------
//! @file	main.cpp
//! @brief	min/maxマクロを無効化するサンプルコード
//! @author つきの
//--------------------------------------------------------------------------
#define NOMINMAX
#include <Windows.h>
#include <algorithm>
// エントリポイント
int main()
{
	// 適当に値を用意する
	int a = 1;
	int b = 2;

	// Windows.hのmin()マクロが邪魔して、std::min()が呼び出せない
	int c = std::min(a, b);
	// プログラムの終了
	return 0;
}

#include <Windows.h>の前にNOMINMAXマクロを定義することで、min/maxマクロが定義されない状態でコンパイルを行うようになります。

知っていても忘れやすく、ヒューマンエラーになる部分なので、実務ではpch.hなどで定義しておくことを強く推奨します。

pch.h
// 実務でのプリコンパイルの例
#define NOMINMAX
#include <Windows.h>

これでプロジェクト全体がmin/maxマクロの影響を受けなくなります。

総括

  • Windows.hにはマクロが大量に定義されており、コードを破壊する可能性がある
  • 中でもmin/maxマクロはstd::minstd::maxと頻繁に衝突をする
  • NOMINMAXを定義することで、Windows.hmin/maxマクロを無効かできる
  • 実務ではプリコンパイルに含めることでヒューマンエラーを回避しやすくなる
2
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?