Давайте возьмем следующий пример программы:
#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: «преобразование»: невозможно преобразовать из «плавающего» в
‘Half_float :: половина’
Так что, похоже, разрешение перегрузки не выбирает версию из пространства имен half_float
(что ADL должен выполнить), но один из std
используя неявное преобразование в float
, Но странно то, что это происходит только для atan2
звоните, а не sin
вызов.
В более крупном проекте, где эта ошибка фактически впервые возникла у меня, она также возникает для других функций с двумя аргументами (или, скорее, с half
аргументы), как fmod
, но не для любой функции с 1 аргументом. Аналогично в более крупном проекте он также отлично работает для gcc 4.6 / 4.7 а также лязг 3.1 без ошибок, хотя я не тестировал эту версию SSCCE явно там.
Так что мой вопрос, это ошибочное поведение на VS 2012сторона (учитывая, что это происходит только для 2012 и только для функции с двумя аргументами), или я наблюдал за некоторыми тонкостями в правилах разрешения перегрузки (которые могут быть немного хитрыми, я думаю)?
РЕДАКТИРОВАТЬ: Также бывает, если я напрямую using namespace half_float
или поместите все это в глобальное пространство имен напрямую. Точно так же это происходит, если я не using namespace std
, но это скорее VS-реализация, помещающая математические функции в глобальное пространство имен.
РЕДАКТИРОВАТЬ: Бывает как с оригиналом ВК 2012 компилятор, а также Ноябрь 2012 ОСАГО этого
РЕДАКТИРОВАТЬ: Хотя я не совсем уверен, что это действительно нарушение стандарта в самом строгом смысле, я подал ошибка поскольку он основан на выводах из моего ответа, поскольку он по меньшей мере не соответствует определению функций с 1 аргументом и заслуживает дальнейшего изучения В.С.-Команда.
Я думаю, что нашел причину. Стандарт C ++ говорит в разделе 26,8 [c.math], что для математических функций библиотеки C,
должны быть дополнительные перегрузки, достаточные для обеспечения:
- Если какой-либо аргумент, соответствующий параметру double, имеет тип long double, то все аргументы, соответствующие параметрам double,
эффективно разыграть в длинный дубль.- В противном случае, если какой-либо аргумент, соответствующий параметру double, имеет тип double или целочисленный тип, то все аргументы, соответствующие
Двойные параметры эффективно приводятся к удвоению.- В противном случае все аргументы, соответствующие двойным параметрам, эффективно приводятся к плавающей точке.
Что также можно увидеть в документация atan2.
Эти перегрузки предоставляются 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 строго нарушает стандарт с его чрезмерно общими функциями или если это жизнеспособный вариант реализации, чтобы обеспечить их.
РЕДАКТИРОВАТЬ: Как кажется, уже есть отчет о дефектах 2086 поскольку стандартная неясная формулировка и исправление находятся в процессе, ограничение требования для этих дополнительных перегрузок только арифметическими типами. Поскольку это, кажется, всегда было первоначальным намерением (и реализовано почти всеми существующими реализациями), и это была просто неясная формулировка, я действительно считаю это ошибкой в VS 2012Реализация.
Я только что попробовал твой код и понял, что с ним не так.
Так как вы не реализовали half::sin
а также half::atan2
, компоновщик все равно выдаст ошибку. Так что если вы реализуете методы half::sin
а также half::atan2
, это должно решить ее (я реализовал их, позволив им вернуть пустую половину, что, конечно, бессмысленно).
После того, как я сделал этот шаг (предоставив (бессмысленную) реализацию двух обязательных методов), сообщения об ошибках почти волшебным образом исчезли.
Может быть, это не решение вашей проблемы, так как я использую GCC, а не VS.
РЕДАКТИРОВАТЬ: Я только что попробовал пример, который я использовал с G ++ с Visual Studio, который дал мне сообщение об ошибке peculier. При условии странности ошибки и кода, работающего с GCC, я должен сделать вывод, что это ошибка в VC2012.
Обходной путь должен специализироваться _Common_float_type
за half
а также half_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>;
}
Обратите внимание, что вы должны специализироваться для всех четырех комбинаций half
а также half_expr
потому что специализация шаблона не учитывает базовые классы.