状況
標準ライブラリ(<algorithm>)のstd::minやstd::maxでエラーが出る!
エラーの例
- Visual Studio Code (エディタ警告)
識別子が必要ですC/C++(40) - GCC
error: expected unqualified-id before '(' token
など...
原因と解決法
Windows APIを使用している場合
このパターンが最も多いでしょう。
原因
実は<Windows.h>(実際には<Minwindef.h>)は以下のようなマクロを定義しています。
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
このマクロが定義されたままstd::maxやstd::minを使用すると、maxやminの部分がマクロに展開されてしまい、
// max
std::(((a) > (b)) ? (a) : (b))
// min
std::(((a) < (b)) ? (a) : (b))
と同じように扱われてしまいます。だからstd::の後に識別子が存在しないように認識されるのです。
解決法
-
NOMINMAX
このようなエラーを回避するためにWindows APIにはNOMINMAXという回避策が用意されています。
これを<Windows.h>をインクルードする前に定義しておくことで回避できます。
#define NOMINMAX // 先に定義しておくことが重要!
#include <Windows.h>
-
()で囲う
C++の機能で、丸括弧で囲っておくとマクロが展開されなくなります。
int a = (std::min)(1, 2); // マクロが展開されない!
これだけで対処できます。一時的に修正したいだけならいいですが、ちゃんと修正するならNOMINMAXを使用することを推奨します。
ちなみに
環境によってはNOMINMAXがなくてもC++であればmin, maxマクロを定義しないことがあったり、インクルードの順番によってエラーが出ないこともあります。このような場合でも安全なコードにしたいなら一応何かしておいた方がよいと思います。
using namespace std;を使用している場合
原因
using namespace std;を使用すると、std::を付けずに標準ライブラリの識別子を使用することができるので便利です。その一方で、名前衝突の原因になることがよくあります。
それによって自分で作成したminやmaxのような識別子と衝突する可能性があります。
using namespace std;は邪悪です。
解決法
-
そもそも
using namespace std;を使用しない
少し面倒かもしれませんが、そもそも使用しないのが最善の解決法です。 -
自分で作った識別子の名前を変える
一時的に修正したいだけなら良いですが、それ以外では推奨しません。
まとめ
- Windows APIを使用している
→#include <Windows.h>より前に#define NOMINMAX -
using namespace std;を使用している
→ そもそも使用しない
大抵の場合これで解決すると思います。