9

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

#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”

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

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

所以我的问题是,这种错误行为是在VS 2012方面(假设它只发生在2012 年并且只发生在 2-argument 函数中),还是我监督了重载解决规则中的一些微妙之处(这确实可以得到一个有点棘手,我猜)?

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

编辑:它发生在原始VC 2012编译器以及它的2012 年 11 月 CTP中。

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

4

3 回答 3

6

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

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

  1. 如果对应于 double 形参的任何实参的类型为 long double,则对应于 double 形参的所有实参都将有效地转换为 long double。
  2. 否则,如果对应于 double 参数的任何实参具有 double 类型或整数类型,则对应于 double 形参的所有实参都将有效地强制转换为 double。
  3. 否则,对应于双参数的所有参数都被有效地转换为浮点数。

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

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

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

所以我们有一个模板函数,其实例化将涉及隐式转换(从half&const half_expr<half>&)和一个可以直接实例化的模板函数。因此后者是优选的。这不会发生在 1-argument 函数中,因为对于那些只需要一个用于整数参数的通用版本,VS 2012只为那些使用 a std::enable_ifof 的函数提供了这个版本std::is_integral

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

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

于 2013-01-12T23:09:19.270 回答
0

我刚刚尝试了你的代码,我发现它有什么问题。

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

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

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


编辑:我刚刚尝试了我在 Visual Studio 中使用 G++ 的示例,这给了我一个更奇怪的错误消息。提供错误的奇怪之处,以及使用 GCC 的代码,我必须得出结论,这是 VC2012 中的错误。

于 2013-01-12T17:58:50.023 回答
0

一种解决方法是专门_Common_float_type用于未定义类型halfhalf_expr以便 SFINAE 摆脱 VS2012 版本的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>;
}

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

于 2013-04-23T11:09:56.250 回答