minmax.cpp
): #include <algorithm>
int main() {
int a = std::min(10, 20);
return 0;
}
Все тривиально и отлично компилируется и в Visual Studio, и в CodeGear/Borland Studio, и Cygwin. Но допустим потребовались какие-то функции из Windows API, и вы подключили файл windows.h
: #include <algorithm>
#include <windows.h>
int main() {
int a = std::min(10, 20);
return 0;
}
Теперь компиляция в 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;
}
Вот теперь в Visual Studio все нормально компилируется. Забавно, что в Cygwin и CodeGear/Borland исходный пример компилируется без проблем. В борландовой версии windows.h
я нашел вот такой фрагмент:
#if defined(__BORLANDC__)
...
# if defined(__cplusplus)
# define NOMINMAX /* for WINDEF.H */
...
# endif
...
#endif /* __BORLANDC__ */
Эдак они заранее оградились от проблемы, принудительно запретив проблемные макросы.Вывод: Порой промежуточные результаты работы препроцессора являются крайне полезной информацией.
На всякий случай напомню, как его запускать для перечисленных мной компиляторов:
Visual Studio:
cl /P имя_исходника.cpp
Borland/CodeGear Studio: cpp32 имя_исходника.cpp
Cygwin: cpp имя_исходника.cpp
Прочие флаги командной строки должны повторять флаги при обычной компиляции. Для препроцессора важны определения макросов (обычно это флаги -D
и -U
) и пути для поиска включаемых файлов (обычно это флаг -I
).Другие посты по теме:
Тут есть один интересный тонкий момент - бороться с этим без #define NOMINMAX/#undef max можно вот так - (std::max)(a, b). Подсмотрено кажется где-то в бусте.
ОтветитьУдалитьArseny Kapoulkine: Действительно, если написать(std::max)(a, b), то макроподстановка не происходит, и все работает.
ОтветитьУдалитьСпасибо.