【问题标题】:std::min/max type deduction different on linux and windowslinux和windows上的std::min/max类型推导不同
【发布时间】:2014-09-25 01:21:43
【问题描述】:

在以下代码中,std::min/max 的模板类型推导似乎很奇怪,我想知道为什么以及如何正确修复它。

以下适用于 Windows VS2013,并在 GCC-4.8 上给出编译错误:(错误见下文)

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

这可以在 GCC-4.8 上编译,但在 VS2013 上会出现编译错误:(错误见下文)

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

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

在 OpenCV 中,cv::Vec3f 是 Vec<float, 3>Vec 的 operator[] 是

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

min/max/floor/ceil 来自 std:: 命名空间(即代码顶部的 using std::min 等)。

所以当我写的时候

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

类型应该是

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

那么为什么 GCC 会在这里纾困?

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'

GCC-4.8 上的错误:

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

答案:

GCC 没有使用 std::floor 而是使用全局命名空间中的 floor(由 cmath 绘制)。如果我添加using std::floor,所有代码都按预期工作!全局命名空间中讨厌的 double floor(...) 函数!

【问题讨论】:

  • MSVC 是正确的:当你调用max(float, double) 时,模板参数无法明确解析。但是,我 cannot reproduce GCC 错误你声称你看到了。
  • 您能提供一个MCVE,我们可以简单地复制和粘贴它并展示您的问题吗?最好没有 OpenCV 依赖。
  • 看这里goo.gl/sxzvnC,玩using std::floor。这就是答案 :-) 我会更新我的帖子。

标签: c++ opencv visual-c++ gcc stl


【解决方案1】:

我记得在 GCC-4.8 的数学实现中,floor 函数返回 double。那就是问题所在。尝试显式转换为 float 或尝试使用您自己的floor

【讨论】:

  • 那要看类型推导了。
  • 是的,但我给它传递了一个浮点数,所以它应该返回一个浮点数。无论如何,在我现在想出来的答案的帮助下,我会更新我的帖子!
【解决方案2】:

如果不是错别字,则在此声明中

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

整数文字9.9 具有double 类型,而其他操作数具有浮点类型。所以编译器无法决定是使用模板参数float还是double

错误信息说清楚了对于函数

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

那里could be 'double' or 'float'

这就是函数调用的样子

std::max( float_value, double_value );

您可以显式指定模板参数,例如

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

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

对于 GCC,它会将返回类型为 double 的标准 C 函数 floor 放在全局命名空间中。

double floor(double x);

所以应用这个函数后的操作数被转换为double类型。但是好像MS VC++没有把这个函数放在全局命名空间中或者MS VC++中的全局命名空间有重载的同名函数。

所以问题在于每个编译器在全局命名空间中放置的函数floor

我认为如果您使用限定名std::floor,那么 GCC 也会发出错误。

所以在你的代码中 MS VC++ 使用函数

float floor(float x);

结果在 GCC 使用函数时出现错误

double floor(double x);

并且函数std::max的所有操作数都有double类型,代码编译成功。:)

【讨论】:

  • 是的,我想我们都同意 MSVC 在这里是正确的这一事实。但是,为什么放“0.0f”时GCC会失败。
  • @Ela782 使用GCC编译代码时,您应该检查您是否确实使用了该算法。
  • “算法”?您是指 中的最小/最大/下限吗?在那种情况下是的,GCC 还应该使用什么? (我尽可能使用 STL,而不是我自己的实现)
  • @@Ela782 我描述了编译器行为不同的原因。
  • 您在答案的第二部分中确定了它!谢谢!
【解决方案3】:

因此,可能让您感到困惑的是 Windows 平台标头中已经存在多年的可怕 hack:

从“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

这很可能意味着,您的 Windows 版本可能使用 #define 宏,而不是您认为的 &lt;algorithm&gt; 的最小/最大值。

如果您明确使用std::minstd::max,这将避免宏扩展,max&lt;T&gt;min&lt;T&gt; 也是如此。就我个人而言,在包含 &lt;windows.h&gt; 或将在我的所有项目中包含该标题的标题之前,我会先做 #define NOMINMAX,这样我就会得到“标准”行为。例如,这是我在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>
...

见知识库143208

当然,这假设有问题的模块以某种方式拉入了&lt;windows.h&gt;...

【讨论】:

  • 是的,我知道宏和 NOMINMAX。如果您需要包含 windows.h,这很方便。但是,我没有包含 windows.h,因为代码完全独立于平台。另外我认为 MSVC 的行为应该如此,而 GCC 的行为很奇怪。
猜你喜欢
  • 2011-08-02
  • 1970-01-01
  • 2016-02-15
  • 2016-08-22
  • 1970-01-01
  • 2012-08-26
  • 1970-01-01
  • 2013-03-09
  • 1970-01-01
相关资源
最近更新 更多