2参数函数的重载解析不正确
Incorrect overload resolution for 2-argument functions
让我们看下面的示例程序:
#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应该完成),而是使用隐式转换到float
的std
的版本。但奇怪的是,这只发生在atan2
调用而不是sin
调用。
在更大的项目中,这个错误实际上首先发生在我身上,它也发生在其他2参数函数(或者更确切地说,那些有2个half
参数的函数),如fmod
,但不适用任何1参数函数。同样,在更大的项目中,gcc 4.6/4.7和clang 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团队进一步调查。
应该有额外的过载,足以确保:
- 如果任何与双形参对应的实参类型为long double,则所有与双形参对应的实参类型均为long double
- 否则,如果任何与双精度形参相对应的实参类型为double或整型,则所有与double形参被有效地转换为double。
- 否则,所有与双形参对应的实参都被有效地强制转换为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_if
或std::is_integral
的函数提供了通用版本。
但是我认为标准对"附加重载"仅为内置类型提供的事实有点不清楚。所以最后,我仍然不确定VS 2012是否严格违反了其过度泛型功能的标准,或者提供这些功能是否是可行的实现选项。
EDIT:看起来,已经有缺陷报告2086标准的措辞不明确,并且正在进行修复,将这些额外的重载要求限制为仅算术类型。因为这似乎一直是最初的意图(并且几乎所有现有的实现都实现了),它只是措辞不清楚,我确实认为这是VS 2012实现中的一个bug。
我刚刚试了一下你的代码,发现了问题所在。
由于没有实现half::sin
和half::atan2
,链接器无论如何都会抛出错误。因此,如果您实现half::sin
和half::atan2
方法,应该可以解决这个问题(我通过让它们返回空的一半来实现它们,当然,这是没有意义的)。
在我执行了这一步(提供两个所需方法的(无意义的)实现)之后,错误消息几乎神奇地消失了。
也许这不是你的问题的解决方案,因为我使用的是GCC,而不是VS.
EDIT:我刚刚尝试了我在visual studio中使用的g++示例,它给了我一个特殊的错误信息。考虑到错误的奇怪性,以及与GCC一起工作的代码,我必须得出结论,这是VC2012中的一个错误。
一种解决方法是将half
和half_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>;
}
请注意,您必须专门化half
和half_expr
的所有四种组合,因为模板专门化不考虑基类。
- 使用2个键的cpp-stl::优先级队列排序不正确
- 取消引用运算符不能重载
- 正弦级数方程计算不正确
- 我试图制作一个程序,要求用户输入问题和答案,但程序循环不正确
- 密码登录程序将永远循环并显示不正确的结果
- 在C++中返回不正确的楼层函数值
- 形状对象的旋转和缩放不正确C++
- 使用 C++ 中的运算符重载显示不正确的结果
- 模板实例化失败:编译器选择不正确的重载函数
- 尝试访问类的私有成员以消除重载是否格式不正确
- 重载解析不正确,const 参数的值为 0
- 为什么编译器在这种情况下选择了不正确的函数重载
- 比较函数使用重载运算符返回不正确的结果
- 在重载解析中,选择使用不明确转换序列的函数是否必然会导致调用格式不正确
- "anti-SFINAE"在给定表达式格式不正确时启用重载
- 设计问题:消息处理组件..调用不正确的重载函数
- 为什么此构造函数重载解析不正确
- MSVC 2015 为 std::vector 选择不正确的构造函数重载
- 如何在Python中重载不正确的c++虚函数时从SWIG中获得正确的错误
- 2参数函数的重载解析不正确