2参数函数的重载解析不正确

Incorrect overload resolution for 2-argument functions

本文关键字:不正确 重载 参数 函数      更新时间:2023-10-16

让我们看下面的示例程序:

#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: 'conversion':不能从'float'转换为"half_float::一半"

因此,似乎重载解析不会选择名称空间half_float的版本(ADL应该完成),而是使用隐式转换到floatstd的版本。但奇怪的是,这只发生在atan2调用而不是sin调用。

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

所以我的问题是,这是错误的行为在VS 2012的一方(考虑到它只发生在2012和仅为2参数函数),或者我监督重载解析规则中的一些微妙之处(这确实可以得到一点棘手,我猜)?

EDIT:它也发生,如果我直接using namespace half_float或把整个东西直接在全局命名空间。同样,如果我不是using namespace std,也会发生这种情况,但这是vs实现将数学函数放在全局命名空间中。

EDIT:它发生在原始VC 2012编译器以及 2012年11月CTP

EDIT:虽然我不完全确定它在最严格的意义上真的违反了标准,但根据我的回答中的发现,我已经为它提交了一个bug,因为它至少与1参数函数的定义不一致,值得VS团队进一步调查。

我想我找到原因了。c++标准在26.8节中规定:,表示C库的数学函数,

应该有额外的过载,足以确保:

  1. 如果任何与双形参对应的实参类型为long double,则所有与双形参对应的实参类型均为long double
  2. 否则,如果任何与双精度形参相对应的实参类型为double或整型,则所有与double形参被有效地转换为double。
  3. 否则,所有与双形参对应的实参都被有效地强制转换为float。

也可以在atan2文档中看到。

这些重载是由VS 2012通过使用一个通用的函数模板来提供的:

template<typename T,typename U> common_float_type<T,U>::type atan2(T, U);
因此,我们有一个模板函数,其实例化将涉及隐式转换(从half&const half_expr<half>&)和一个模板函数,可以直接实例化。因此,后者是首选。这种情况不会发生在单参数函数中,因为对于这些函数,只有整数参数的通用版本,VS 2012仅为使用std::enable_ifstd::is_integral的函数提供了通用版本。

但是我认为标准对"附加重载"仅为内置类型提供的事实有点不清楚。所以最后,我仍然不确定VS 2012是否严格违反了其过度泛型功能的标准,或者提供这些功能是否是可行的实现选项。

EDIT:看起来,已经有缺陷报告2086标准的措辞不明确,并且正在进行修复,将这些额外的重载要求限制为仅算术类型。因为这似乎一直是最初的意图(并且几乎所有现有的实现都实现了),它只是措辞不清楚,我确实认为这是VS 2012实现中的一个bug。

我刚刚试了一下你的代码,发现了问题所在。

由于没有实现half::sinhalf::atan2,链接器无论如何都会抛出错误。因此,如果您实现half::sinhalf::atan2方法,应该可以解决这个问题(我通过让它们返回空的一半来实现它们,当然,这是没有意义的)。

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

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


EDIT:我刚刚尝试了我在visual studio中使用的g++示例,它给了我一个特殊的错误信息。考虑到错误的奇怪性,以及与GCC一起工作的代码,我必须得出结论,这是VC2012中的一个错误。

一种解决方法是将halfhalf_expr_Common_float_type特化为未定义的类型,以便SFINAE摆脱atan2的VS2012版本。

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>;
}

请注意,您必须专门化halfhalf_expr的所有四种组合,因为模板专门化不考虑基类。