我们采取以下示例程序:
#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。
答案 0 :(得分:6)
我想我找到了原因。 C ++标准在 26.8 [c.math] 部分中说明,对于C库的数学函数,
应该有足够的额外超载来确保:
- 如果对应于double参数的任何参数的类型为long double,则所有与double参数对应的参数都是 有效地施展到长双。
- 否则,如果对应于double参数的任何参数具有double类型或整数类型,则所有参数都对应于 双参数有效地加倍。
- 否则,所有与double参数对应的参数都被有效地转换为float。
醇>
这也可以在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::sin
和half::atan2
,链接器无论如何都会抛出错误。因此,如果您实现方法half::sin
和half::atan2
,那应该解决它(我通过让它们返回空半部分实现它们,当然,这是无意义的)。
在我采取这一步骤(提供两个必需方法的(无意义)实现)后,错误消息几乎神奇地消失了。
也许这不是你问题的解决方案,因为我使用的是GCC,而不是VS.
编辑:我刚试了一下我用G ++和visual studio的样本,这给了我一个很好的错误信息。如果错误的陌生性和使用GCC的代码,我必须得出结论,这是VC2012中的一个错误。
答案 2 :(得分:0)
解决方法是将_Common_float_type
和half
的{{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
的所有四种组合,因为模板专精化不考虑基类。