Это сложная тема, и ответить на ваш вопрос непросто.
Во всяком случае, некоторые наблюдения / предложения, без всякой претензии на исчерпывающий характер.
(1) путь static_assert()
static_assert(LessThanComparable<U,T>, "oh this is not epic");
- хорошее решение, если вам нужна функция, которая работает только с некоторыми типами и выдает ошибку (явную ошибку, потому что вы можете выбрать сообщение об ошибке) при вызове с неправильными типами.
Но обычно это неправильное решение, когда вам нужна альтернатива. Это не решение SFINAE. Таким образом, вызов функции с аргументами неправильного типа дает ошибку и не позволяет использовать другую функцию для подстановки.
(2) Вы правы насчет
typename = std::enable_if_t</* some test */>>
решение. Пользователь может вручную указать третий параметр. В шутку говорю, что это решение можно «угнать».
Но это не единственный недостаток этого решения.
Предположим, у вас есть две дополнительные foo()
функции, которые необходимо включить / отключить через SFINAE; первый, когда тест верен, второй, когда тот же тест, если ложный.
Вы можете подумать, что следующее решение опасно (может быть взломано), но может работать
/* true case */
template <typename T, typename = std::enable_if_t<true == some_test_v<T>>>
void foo (T t)
{ /* something with t */ }
/* false case */
template <typename T, typename = std::enable_if_t<false == some_test_v<T>>>
void foo (T t)
{ /* something with t */ }
Неправильно: это решение просто не работает, потому что вы включаете / отключаете не второе имя типа, а только значение по умолчанию для второго имени типа. Таким образом, вы не полностью включаете / отключаете функции, и компилятор должен учитывать две функции с одинаковой сигнатурой (сигнатура функции не зависит от значений по умолчанию); значит, вы столкнулись с ошибкой.
Следующие решения, SFINAE по возвращаемому типу
std::enable_if_t<LessThanComparable<U,T>, void> order(T& a, U& b)
(также без void
, это тип по умолчанию
std::enable_if_t<LessThanComparable<U,T>> order(T& a, U& b)
) или над вторым типом (следуя предложению Якка о нестандартном разрешенном void *
)
template <typename T, typename U,
std::enable_if_t<LessThanComparable<U,T>>, bool> = true>
являются (ИМХО) хорошими решениями, потому что оба они избегают риска угона и совместимы с двумя дополнительными функциями с тем же именем и подписью.
Я предлагаю третье возможное решение (без возможности взлома, дополнительная совместимость), которое представляет собой добавление третьего значения по умолчанию с включенным / отключенным типом SFINAE: что-то вроде
template <typename T, typename U>
void order(T& a, U& b, std::enable_if_t<LessThanComparable<U,T>> * = nullptr)
Другое возможное решение - вообще избегать SFINAE, но использовать диспетчеризацию тегов; что-то как
template <typename T, typename U>
void order_helper (T & a, U & b, std::true_type const &)
{ if (b < a) { std::swap(a, b); } }
// something different if LessThanComparable is false ?
template <typename T, typename U>
void order_helper (T & a, U & b, std::false_type const &)
{ /* ???? */ }
template <typename T, typename U>
void order (T & a, U & b)
{ order_helper(a, b, LessThanComparable<U,T>{}); }
Это в случае, если LessThanComplarable
наследуется от std::true_type
, когда условие истинно, от std::false_type
, когда условие ложно.
В противном случае, если LessThanComparable
дает только логическое значение, вызов order_helper()
может быть
order_helper(a, b, std::integral_constant<bool, LessThanComparable<U,T>>{});
(3) если вы можете использовать C ++ 17, существует if constexpr
способ избежать большой перегрузки.
template <typename T, typename U>
void order(T& a, U& b)
{
if constexpr ( LessThanComparable<U, T> )
{
if ( b < a )
std::swap(a, b);
}
else
{
// what else ?
}
}
person
max66
schedule
25.12.2018
LessThanComparable
? - person L. F.   schedule 25.12.2018