Ошибка замены

В настоящее время я пытаюсь написать какую-то гибкую математическую библиотеку времени компиляции и только что столкнулся с ошибкой замещения, от которой мне не удалось избавиться. Вот проблема:

Прежде всего, я пишу рациональный класс, я поставлю единственную часть, которая нужна.

template<typename T>
class rational
{
static_assert(std::is_integral<T>::value, "Can only contain integral values.");

public:

constexpr rational(T numerator, T denominator);

private:

T _numerator;
T _denominator;
};

И чтобы библиотека была гибкой, я пытался интенсивно использовать SFINAE, чтобы ограничить вызовы функций оператора только рационально-рациональными, рационально-интегральными и интегрально-рациональными, но которые работали бы независимо от интеграла и лежащего в основе тип интеграла есть. Вот объявления функций для operator+ например:

template<typename T, typename U>
constexpr
rational<typename std::common_type<T, U>::type>
operator+(const rational<T>& lhs, const rational<U>& rhs);

template<typename T, typename U>
constexpr
typename std::enable_if<std::is_integral<U>::value, rational<typename std::common_type<T, U>::type>>::type
operator+(const rational<T>& lhs, const U& rhs);

template<typename T, typename U>
constexpr
typename std::enable_if<std::is_integral<T>::value, rational<typename std::common_type<T, U>::type>>::type
operator+(const T& lhs, const rational<U> rhs);

А вот и неисправный сегмент кода. Он не падает из-за static_assert но предположительно из-за неудачи замещения:

constexpr auto r1 = rational<int>(1, 2);
constexpr auto r2 = rational<int>(2, 4);
static_assert(r1 + r2 == rational<int>(1, 1), "");

Ошибка следующая (я сохранил только ошибки без окружающих блабл):

... required by substitution of 'template<class T, class U> constexpr typename std::enable_if<std::is_integral<T>::value, smath::rational<typename std::common_type<_Tp, _Up>::type> >::type smath::operator+(const T&, smath::rational<U>) [with T = smath::rational<int>; U = int]'
... required from here
... error: operands to ?: have different types 'smath::rational<int>' and 'int'
... required by substitution of 'template<class T, class U> constexpr typename std::enable_if<std::is_integral<U>::value, smath::rational<typename std::common_type<_Tp, _Up>::type> >::type smath::operator+(const smath::rational<T>&, const U&) [with T = int; U = smath::rational<int>]'
... required from here
... error: operands to ?: have different types 'int' and 'smath::rational<int>'

Я предполагал, что g ++ выберет первую шаблонную функцию, которая работает с двумя рациональными числами и будет в порядке. Тем не менее, кажется, что он все еще пытается применить последние две функции и терпит неудачу при попытке сделать это. Это я не могу понять. Некоторая помощь приветствовалась бы 🙂

РЕДАКТИРОВАТЬ: Кажется, что имея rational конструктор explicit решает проблему, и это здорово. Тем не менее, мне все еще интересно знать, почему замена не удалась так сложно.

0

Решение

Проблема в том, что тип передан std::enable_if<whatever, T>, Даже если замена провалится, аргумент должен быть разумным, но это не так. Итак, использование typename std::common_type<T, U>::type не работает, если нет такого типа для потенциально оцениваемых типов. Вам нужно что-то еще. Что работает, так это создание ошибки замещения, отключающей смешанные целочисленные / рациональные перегрузки в списке аргументов шаблона:

template<typename T, typename U, typename = typename std::enable_if<std::is_integral<U>::value, void>::type>
constexpr
rational<typename std::common_type<T, U>::type>
operator+(const rational<T>& lhs, const U& rhs);

template<typename T, typename U, typename = typename std::enable_if<std::is_integral<T>::value, void>::type>
constexpr
rational<typename std::common_type<T, U>::type>
operator+(const T& lhs, const rational<U> rhs);

Прямо сейчас я не совсем уверен, является ли это обходным решением проблемы gcc или необходимо ли это делать таким образом.

1

Другие решения

Других решений пока нет …