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

Chr*_*ica 9 c++ overload-resolution visual-studio-2012

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

#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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

所以似乎重载解析不会从命名空间中选择版本half_float(ADL应该完成),而是std使用隐式转换来实现float.但奇怪的是,这只发生在atan2通话而非sin通话中.

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

所以我的问题是,这是VS 2012的错误行为(假设它只发生在2012年,仅用于2参数函数),或者我是否监督了重载决策规则中的一些细微之处(这确实可以得到一个有点棘手,我想)?

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

编辑:它与原始的VC 2012编译器以及2012年11月的CTP一起发生.

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

Chr*_*ica 6

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

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

  1. 如果对应于double参数的任何参数的类型为long double,则对应于double参数的所有参数都有效地转换为long double.
  2. 否则,如果对应于double参数的任何参数具有double类型或整数类型,则对应于double参数的所有参数都将有效地转换为double.
  3. 否则,对应于double参数的所有参数都被有效地转换为float.

这也可以在atan2文档中看到.

VS 2012通过使用以下形式的通用函数模板提供这些重载:

template<typename T,typename U> common_float_type<T,U>::type atan2(T, U);
Run Code Online (Sandbox Code Playgroud)

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

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

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