Tom*_*don 10 c++ visual-c++ c++11 visual-studio-2012
我有一些示例代码在Visual C++ 2012下使用新的C++ 11头文件的行为与VC++ 2010下的代码不同.它涉及当您调用包含cmath时获得的std :: fmod函数时会发生什么,当你传递的参数不是双精度数,而是具有隐式转换为double运算符的类时:
#include <cmath>
class Num {
double d_;
public:
Num(double d) : d_(d) {}
operator double() const { return d_; }
};
int main(int argc, char* argv[]) {
Num n1(3.14159265358979323846264338327950288419716939937510);
Num n2(2.0);
double result1 = fmod((double)n1, (double)n2);
double result2 = fmod((float)n1, (float)n2);
double result3 = fmod(n1, n2);
if (result2==result1) std::cout << "fmod(double, double) returns the same as fmod(float,float)" << std::endl;
if (result3==result1) std::cout << "fmod(Num, Num) returns the same as fmod(double,double)" << std::endl;
if (result3==result2) std::cout << "fmod(Num, Num) returns the same as fmod(float,float)" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
更令我惊讶的是,这会调用带有两个浮点数的fmod版本,而不是需要两个双打的fmod版本.
所以我的问题是,这是C++ 11标准的正确行为吗?我可以找到关于该问题的唯一信息是cppreference.com文档中在这里,它说(重点煤矿):
如果任何参数具有整数类型,则将其强制转换为double.如果任何其他参数是long double,则返回类型为long double,否则为double.
但是,Visual Studio头文件中的实现似乎实现"否则它是一个浮点数".
任何人都知道意图是什么:-)?
通过在线c ++ 11版本的GCC运行示例(我没有简单访问最近的GCC副本),它似乎是调用fmod的"双"版本,这是我天真的期望.
为清楚起见,我正在使用
用于x86的Microsoft(R)C/C++优化编译器版本17.00.51106.1
这就是随之而来的
Microsoft Visual Studio Express 2012 for Windows桌面版本11.0.51106.01更新1
这与我的这个问题有关.原因是,为了提供标准所要求的额外重载(并在您的问题中引用),VS 2012定义了所有2参数数学函数的通用函数模板.所以你实际上并没有打电话,fmod(float, float)而是fmod<Num>(Num, Num)首先打电话.
这个模板函数优先于普通的原因double版本是,因为双版本将要求从用户定义的转换Num到double,而模板版本是直接实例化的.
但是,调用fmod函数的实际基本类型由此类型特征决定<xtgmath.h>:
template<class _Ty>
struct _Promote_to_float
{ // promote integral to double
typedef typename conditional<is_integral<_Ty>::value,
double, _Ty>::type type;
};
template<class _Ty1,
class _Ty2>
struct _Common_float_type
{ // find type for two-argument math function
typedef typename _Promote_to_float<_Ty1>::type _Ty1f;
typedef typename _Promote_to_float<_Ty2>::type _Ty2f;
typedef typename conditional<is_same<_Ty1f, long double>::value
|| is_same<_Ty2f, long double>::value, long double,
typename conditional<is_same<_Ty1f, double>::value
|| is_same<_Ty2f, double>::value, double,
float>::type>::type type;
};
Run Code Online (Sandbox Code Playgroud)
这样做是检查提升类型_Promote_to_float(在你的情况下再次Num,因为它只检查它的积分,Num显然不是),直到它匹配所有浮点类型,它不匹配,从而导致否则的情况float.
这种错误行为的原因是,那些额外的数学重载从来都不是为每种类型提供的,而是仅针对内置算术类型(并且有关标准措辞即将修复,如我在回答中所述)相关问题).因此,在上面所解释的所有这种类型的演绎机制中,VS 2012假定传入的类型是内置的整数类型,或者如果不是,则内置浮点类型,这当然是失败的Num.所以实际问题是VS提供了过于通用的数学函数,而它们应该只为内置类型提供重载(就像已经为1参数函数正确完成的那样).如链接的答案中所述,我已经为此提交了一个错误.
编辑: 事实上(正如你也意识到的那样)即使他们会遵循当前模糊的标准措辞并且需要提供通用功能模板,他们仍然应该将这些通用参数的实际提升类型定义为double而不是float.但我认为这里的实际问题是它们完全忽略了在整个类型转换过程中可能存在的非内置类型(因为对于内置类型,它们的逻辑非常好).
但是根据目前模糊的标准措辞(虽然已经计划改变),在第26.8节[c.math]中,他们确实正确地推断了推广类型float(按照第3种情况):
应有足够的额外过载以确保:
- 如果对应于double参数的任何参数的类型为long double,则对应于double参数的所有参数都有效地转换为long double.
- 否则,如果对应于double参数的任何参数具有double类型或整数类型,则对应于double参数的所有参数都将有效地转换为double.
- 否则,对应于double参数的所有参数都被有效地转换为float.
| 归档时间: |
|
| 查看次数: |
1158 次 |
| 最近记录: |