2参数函数的重载分辨率不正确

时间:2013-01-12 16:13:32

标签: c++ visual-studio-2012 overload-resolution

我们采取以下示例程序:

#include <cmath>

namespace half_float
{
    template<typename T> struct half_expr {};

    struct half : half_expr<half>
    {
        operator float() const;
    };

    template<typename T> half sin(const half_expr<T>&);
    template<typename T> half atan2(const half_expr<T>&, const half_expr<T>&);
}

using namespace std;
using half_float::half;

int main()
{
    half a, b;
    half s = sin(a);
    half t = atan2(a, b);
}

VS 2010 中,编译得很好(暂时忽略明显的链接器错误)。但是在 VS 2012 中,这给了我:

  

错误C2440:'转换':无法从'float'转换为   'half_float ::半'

因此,似乎重载解析不会从命名空间half_float(ADL应该完成)中选择版本,而是std使用隐式转换为float的版本。但奇怪的是,这只发生在atan2来电,而不是sin来电。

在较大的项目中,这个错误实际上首先发生在我身上,它也发生在其他2参数函数(或者更确切地说是那些有half个参数的函数)上,例如fmod,但不适用于任何1参数函数。同样在较大的项目中,它也适用于 gcc 4.6 / 4.7 clang 3.1 而没有错误,但我没有明确地测试这个SSCCE版本。

所以我的问题是,这是 VS 2012 方面的错误行为(鉴于它只发生在 2012 并且仅针对2参数函数),或者我是否监督了重载决策规则中的一些细微之处(我猜这确实有点棘手)?

编辑:如果我直接using namespace half_float或直接将全部内容放在全局命名空间中,也会发生这种情况。同样,如果我不是using namespace std,它也会发生,但这是将数学函数放在全局命名空间中的VS实现。

编辑:原始的 VC 2012 编译器以及 2012年11月的CTP 都会发生这种情况。

编辑:虽然我并不完全确定它是否真的违反了严格意义上的标准,但我根据答案中的调查结果提交了bug ,因为它至少与1参数函数的定义不一致,值得进一步研究 VS -Team。

3 个答案:

答案 0 :(得分:6)

我想我找到了原因。 C ++标准在 26.8 [c.math] 部分中说明,对于C库的数学函数,

  

应该有足够的额外超载来确保:

     
      
  1. 如果对应于double参数的任何参数的类型为long double,则所有与double参数对应的参数都是   有效地施展到长双。
  2.   
  3. 否则,如果对应于double参数的任何参数具有double类型或整数类型,则所有参数都对应于   双参数有效地加倍。
  4.   
  5. 否则,所有与double参数对应的参数都被有效地转换为float。
  6.   

这也可以在atan2 documentation中找到。

VS 2012 通过使用表格的通用功能模板提供这些重载:

template<typename T,typename U> common_float_type<T,U>::type atan2(T, U);

所以我们有一个模板函数,它的实例化涉及一个隐式转换(从half&const half_expr<half>&)和一个可以直接实例化的模板函数。因此后者是优选的。对于1参数函数不会发生这种情况,因为对于那些只需要为积分参数的通用版本,由 VS 2012 提供仅适用于使用std::enable_if std::is_integral

但我认为标准有点不清楚,那些“额外重载”只能用于内置类型。所以最后我仍然不确定 VS 2012 是否严格违反了标准及其过于通用的功能,或者它是否是提供这些功能的可行实施选项。

编辑:看起来,标准的措辞不清楚已经defect report 2086,并且正在进行修复,将这些额外重载的要求仅限于算术类型。由于这似乎一直是最初的意图(并且几乎所有现有的实现都已实现)并且仅仅是不清楚的措辞,我确实认为这是 VS 2012 的实现中的错误。

答案 1 :(得分:0)

我刚尝试了你的代码,我发现了它的错误。

由于您尚未实现half::sinhalf::atan2,链接器无论如何都会抛出错误。因此,如果您实现方法half::sinhalf::atan2,那应该解决它(我通过让它们返回空半部分实现它们,当然,这是无意义的)。

在我采取这一步骤(提供两个必需方法的(无意义)实现)后,错误消息几乎神奇地消失了。

也许这不是你问题的解决方案,因为我使用的是GCC,而不是VS.


编辑:我刚试了一下我用G ++和visual studio的样本,这给了我一个很好的错误信息。如果错误的陌生性和使用GCC的代码,我必须得出结论,这是VC2012中的一个错误。

答案 2 :(得分:0)

解决方法是将_Common_float_typehalf的{​​{1}}专门化为未定义类型,以便SFINAE摆脱VS2012版half_expr

atan2

请注意,您必须专注于namespace std { template<class T1, class T2> struct _Common_float_type<half_float::half_expr<T1>, half_float::half_expr<T2>>; template<class T2> struct _Common_float_type<half_float::half, half_float::half_expr<T2>>; template<class T1> struct _Common_float_type<half_float::half_expr<T1>, half_float::half>; template<> struct _Common_float_type<half_float::half, half_float::half>; } half的所有四种组合,因为模板专精化不考虑基类。