Friday, February 29, 2008

std::min and std::max

Today I typed the following:
int t = (std::max)(timeout, lagtime);

Why did I put parentheses around std::max? Because windows.h defines (among other things) a max and a min macro. If you include windows.h the above code will not compile. For example the following:
#include "windows.h"
#include <algorithm>

void foo() {
int i = 5;
int j = 7;
int x = std::max(i,j);

Will produce the following error with Visual Studio C++ 2005:
1>test.cpp(7) : error C2589: '(' : illegal token on right side of '::'
1>test.cpp(7) : error C2143: syntax error : missing ';' before '::'

There are a number of ways to work around windows.h defining these two macros.

  • Use alternative names defined in windows.h.
    int x = _cpp_max(i,j);
    int y = _cpp_min(i,j);
    This is not portable; only works on Windows.

  • Define NOMINMAX before including windows.h. This might break existing code that assumes NOMINMAX is not defined.

  • Don't use std::min and std::max. Instead use the tertiary operator like so:
    int x = i > j ? i : j; // max(i,j)
    int y = i < j ? i : j; // min(i,j)
    This is portable but not as readable and more error prone.

  • Use using statements to make the code portable:
    using std::min;
    using std::max;
    int x = max(i,j);
    int y = min(i,j);
    This works but requires two more lines of code. You could also just use 'using namespace std;' but that might pull in more than you want.

  • Use std::min<int> and std::max<int>
    int x = std::max<int>(i,j);
    int y = std::min<int>(i,j);
    This requires you to specify the type. However in some cases this actually helps. For example:
    int i = 5;
    unsigned int j = 7;
    int x = (std::max)(i,j);
    int y = (std::min)(i,j);
    Note the 'unsigned'. Generates the following errors:
    1>test.cpp(7) : error C2780: 'const _Ty &std::max(const _Ty &,const _Ty &,_Pr)' : 
    expects 3 arguments - 2 provided
    1> c:\program files\microsoft visual studio 8\vc\include\xutility(3190) :
    see declaration of 'std::max'
    1>test.cpp(7) : error C2782: 'const _Ty &std::max(const _Ty &,const _Ty &)' :
    template parameter '_Ty' is ambiguous
    1> c:\program files\microsoft visual studio 8\vc\include\xutility(3182) :
    see declaration of 'std::max'
    1> could be 'unsigned int'
    1> or 'int'
    By explicitly specifying type via <int> you remove the ambiguity.

  • Use (std::min) and (std::max)
    int i = 5;
    int j = 7;
    int x = (std::max)(i,j);
    int y = (std::min)(i,j);
    This works (as does the std::max<int>) because the C++ preprocessor requires '(' as the next preprocessing token following the macro name to preform the macro expansion.


Justin said...

I usually just #undef min/max after including windows.h. At least that's how I remember it, although it's been several years since I was a c++ programmer. :(

Eden Crane said...

Thank you for reporting this. I am new to Windows programming. I had this error and I was very confused. I want to write portable code, and this article describes many options available.

Anonymous said...

Great article.

Quicke said...

Thanks for these solutions. Without this article I probably wouldn't have found the origin of this compile error.

Linda said...

Thank you. This was really helpful!

Anonymous said...

another possibility to trick the preprocessor ist the insertion of an empty macro.
But this also reads not very nice.

#define ____
std::max ____ (a,b)

Volkan OZYILMAZ said...

Thank you. It is very useful. :)

Chad Webb said...

Thanks! I too try to write portable code. I added windows.h at a later time in my program for one test platform which caused the errors that never showed up previously. Thanks to your article, I was able to quickly resolve the issue.

cbamber85 said...

I'm normally a Linux programmer and this compiler error had me going for a full day - damn MS!

Thanks for the solution!

Anonymous said...

Very helpful, thanks!

Anonymous said...

#undef works fine.Thanks

Anonymous said...

As solution, I would suggest switching to a standard compliant compiler.

Anonymous said...

Oh God... This 2008 article helps a Chinese programmer today. Thanks man!