Ответ 1
Это связано с этим вопросом. Причина в том, что для обеспечения дополнительных перегрузок, требуемых стандартом (и цитируемых в вашем вопросе), VS 2012 определяет общие шаблоны функций для всех математических функций с 2 аргументами. Таким образом, вы на самом деле не называете fmod(float, float)
, но fmod<Num>(Num, Num)
.
Причина, по которой эта шаблонная функция предпочтительнее, чем обычная версия double
, потому что для двойной версии потребуется определяемое пользователем преобразование от Num
до double
, тогда как версия шаблона непосредственно инстантабельная.
Но фактический фундаментальный тип для вызова функции fmod
for для этого определяется по типу этого типа от <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;
};
Что это такое - проверьте продвинутый тип _Promote_to_float
(который в вашем случае снова Num
, потому что он проверяет, не является ли его интеграл, который Num
явно) для всех типов с плавающей запятой, пока он не будет совпадений, которых нет, и, следовательно, приводит к случаю else float
.
Причиной этого ошибочного поведения является то, что эти дополнительные математические перегрузки никогда не предназначались для каждого типа, но только для встроенных арифметических типов (и однозначная стандартная формулировка должна быть исправлена, как указано в мой ответ на связанный вопрос). Таким образом, во всем этом методе дедукции, описанном выше, VS 2012 предполагает, что пройденные типы являются либо встроенными интегральными типами, либо, если нет, то встроены типы с плавающей запятой, что, конечно же, не выполняется для Num
. Таким образом, фактическая проблема заключается в том, что VS предоставляет слишком общие математические функции, тогда как они должны обеспечивать только перегрузки для встроенных типов (например, правильно выполненные для функций с 1 аргументом). Как указано в связанном ответе, я уже подал на него ошибку.
РЕДАКТИРОВАТЬ: Фактически (как вы поняли), даже если бы они выполняли текущую двусмысленную стандартную формулировку и предоставляли общие шаблоны функций, они все равно должны были определить фактический продвинутый тип из этих общих аргументов как double
вместо float
. Но я думаю, что настоящая проблема заключается в том, что они полностью игнорируют возможное наличие нестроенных типов в этом процессе преобразования всего типа (поскольку для встроенных типов их логика отлично работает).
Но в соответствии с текущей двусмысленной стандартной формулировкой (которая уже запланирована для изменения) в разделе 26.8 [c.math] они действительно правильно выведут продвинутый тип как float
(per 3-й случай):
должны быть дополнительные перегрузки, достаточные для обеспечения:
- Если любой аргумент, соответствующий двойному параметру, имеет тип long double, то все аргументы, соответствующие двойным параметрам, эффективно отбрасывается в длинный двойной.
- В противном случае, если любой аргумент, соответствующий двойному параметру, имеет тип double или integer, то все аргументы, соответствующие двойные параметры эффективно дублируются.
- В противном случае все аргументы, соответствующие двойным параметрам, эффективно применяются для float.