Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::min/max type deduction different on linux and windows

In the following code, the template type deduction for std::min/max seems to be weird and I'd like to know why and how to properly fix it.

The following works on Windows VS2013, and gives a compile-error on GCC-4.8: (see below for the error)

int minX = max(min(floor(v1[0]), min(floor(v2[0]), floor(v3[0]))), 0.0f);

And this compiles on GCC-4.8 but gives a compile-error on VS2013: (see below for the error)

int minX = max(min(floor(v1[0]), min(floor(v2[0]), floor(v3[0]))), 0.0);

v[1-3] are a cv::Vec3f v1, v2, v3;

from OpenCV, a cv::Vec3f is a Vec<float, 3> and Vec's operator[] is

const _Tp& operator [](int i) const;
_Tp& operator[](int i);

The min/max/floor/ceil are from the std:: namespace (i.e. using std::min etc. at the top of the code).

So when I write

int minX = max(min(floor(v1[0]), min(floor(v2[0]), floor(v3[0]))), 0.0f);

the types should be

max(min(float, min(float, float), float);

So why does GCC bail out here?

Error on VS2013:

error C2782: 'const _Ty &std::max(const _Ty &,const _Ty &)' : template parameter '_Ty' is ambiguous
          C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\algorithm(4086) : see declaration of 'std::max'
          could be 'double' or 'float'

Error on GCC-4.8:

error: no matching function for call to 'max(const double&, float)'

Answer:

GCC wasn't using std::floor but floor from the global namespace (drawn in by cmath). If I add using std::floor, all the code works as expected! Nasty double floor(...) function in the global namespace!

like image 359
Ela782 Avatar asked Aug 01 '14 19:08

Ela782


2 Answers

If it is not a typo then in this statement

int minX = max(min(floor(v1[0]), min(floor(v2[0]), floor(v3[0]))), 0.0);

integer literal 9.9 has type double while other operands have type float. So the compiler can not decide whether to use template argument float or double

The error message says clear that for function

'const _Ty &std::max(const _Ty &,const _Ty &)' 

there could be 'double' or 'float'

That is the funcrion call looks as

std::max( float_value, double_value );

You could explicitly specify the template argument as for example

std::max<double>( float_value, double_value );

or

std::max<float>( float_value, double_value );

As for GCC then it places the standard C function floor that has return type double in the global namespace..

double floor(double x);

So the operands after applying this function are converted to the type double. But it seems that MS VC++ does not places this function in the global namespace or the global namespace in MS VC++ has overloaded functions with the same name.

So the problem is relative to what function(s) floor each compiler places in the global namespace.

I think that if you used qualified name std::floor then GCC would also issue an error.

So in your code MS VC++ uses function

float floor(float x);

and as the result issues the error while GCC uses function

double floor(double x);

and all operands of function std::max have type double and the code is compiled successfully.:)

like image 146
Vlad from Moscow Avatar answered Oct 02 '22 01:10

Vlad from Moscow


So the thing that is probably confusing you is a terrible hack in the Windows platform headers that has been there for ages:

From deep inside "ntdef.h"

#ifndef NOMINMAX

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

#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif

#endif  // NOMINMAX

That means in all likelihood, your Windows build is probably using the #define macro and not <algorithm>'s min/max as you think it is.

If you use std::min or std::max explicitly, this will avoid the macro expansion, as would max<T> and min<T>. Personally I make a point of doing #define NOMINMAX before including <windows.h> or headers that are going to include that header in all my projects so I get "standard" behavior. For example, a this is the Precompiled Header I use in DirectX Tool Kit:

#if !defined(WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
#endif

#if !defined(NOMINMAX)
#define NOMINMAX
#endif

#include <d3d11_1.h>

#include <DirectXMath.h>

#include <algorithm>
...

See KB 143208

Of course, this assumes that the module in question has pulled <windows.h> in somehow...

like image 30
Chuck Walbourn Avatar answered Oct 01 '22 23:10

Chuck Walbourn