Простейший кусок кода (файл minmax.cpp
):
#include <algorithm> int main() { int a = std::min(10, 20); return 0; }
Все тривиально и отлично компилируется и в Visual Studio, и в CodeGear/Borland Studio, и Cygwin. Но допустим потребовались какие-то функции из Windows API, и вы подключили файл windows.h
:
Теперь компиляция в Visual Studio (я проверял в 2005 и 2008) будет падать со следующей ошибкой:
minmax.cpp
minmax.cpp(4) : error C2589: '(' : illegal token on right side of '::'
minmax.cpp(4) : error C2059: syntax error : '::'
Постановка #include <windows.h>
до #include <algorithm>
проблемы не решает.
Очевидно, проблема в том, что кто-то переопределил значение слова min. Запустим препроцессор и проверим догадку:
cl /P minmax.cpp
И что мы видим? А видим мы следующее (фрагмент файла minmap.i
):
#line 7 "minmax.cpp" int main() { int a = std::(((10) < (20)) ? (10) : (20)); return 0; }
Естественно, это каша с точки зрения синтаксиса, и компилятор ругается совершенно законно.
Покопавшись в заголовочных файлах Windows SDK, в файле WinDef.h
, который косвенно подключается через windows.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 /* NOMINMAX */
Вот теперь ясно, что делать — надо определить макрос NOMINMAX, тем самым заблокировать определение min
и max
:
#define NOMINMAX #include <algorithm> #include <windows.h> int main() { int a = std::min(10, 20); return 0; }
Забавно, что в Cygwin и CodeGear/Borland исходный пример компилируется без проблем. В борландовой версии windows.h
я нашел вот такой фрагмент:
#if defined(__BORLANDC__) ... # if defined(__cplusplus) # define NOMINMAX /* for WINDEF.H */ ... # endif ... #endif /* __BORLANDC__ */
Эдак они заранее оградились от проблемы, принудительно запретив проблемные макросы.
Вывод: Порой промежуточные результаты работы препроцессора являются крайне полезной информацией.
На всякий случай напомню, как его запускать для перечисленных мной компиляторов:
Visual Studio:
cl.exe /P имя_исходника.cpp
Borland/CodeGear Studio:
cpp32.exe имя_исходника.cpp
Cygwin:
cpp.exe имя_исходника.cpp
Прочие флаги командной строки должны повторять флаги при обычной компиляции. Для препроцессора важны определения макросов (обычно это флаги -D
и -U
) и пути для поиска включаемых файлов (обычно это флаг -I
).
Другие посты по теме: