Вот пример сокращенного примера:
template <typename T, typename R, typename=std::enable_if_t<std::is_same_v<R, int>>
void foo(T, R); // #1
template <typename T, typename R>
void foo(T, R); // #2
foo(some_t{}, some_r{});
На самом деле не имеет значения, какое конкретно ограничение sfinae, поэтому я выбрал простой.Теперь, если ограничение (в данном случае is_same_v<R, int>
) равно , а не , замена на #1
не выполняется, и у нас остается один единственный кандидат: #2
.
Ноесли замена удалась, у нас есть два кандидата.Они в равной степени жизнеспособны, их нечего отличить.Следовательно, вызов неоднозначен!
Нам нужен дополнительный способ их различения.Одним из способов является добавление отрицательного ограничения к другой перегрузке (обратите внимание, что вам нужно изменить форму SFINAE здесь, чтобы эта работа работала):
template <typename T, typename R, std::enable_if_t<std::is_same_v<R, int>, int> = 0>
void foo(T, R); // #1
template <typename T, typename R, std::enable_if_t<!std::is_same_v<R, int>, int> = 0>
void foo(T, R); // #2
Это обеспечивает жизнеспособность ровно одного из двух.
Другим способом было бы переадресация черты в отдельный набор перегрузки:
template <typename T, typename R>
void foo_impl(T, R, std::true_type); // #1
template <typename T, typename R>
void foo_impl(T, R, std::false_type); // #2
template <typename T, typename R>
void foo(T t, R r) {
return foo_impl(t, r, std::is_same<R, int>{}); // NB: not _v
}
Другим способом было бы просто написать одну перегрузку и использовать if constexpr
внутри:
template <typename T, typename R>
void foo(T, R) {
if constexpr (std::is_same_v<R, int>) {
// #1
} else {
// #2
}
}
Будущий способ (в C ++ 20) будет использовать понятия:
template <typename T, typename R>
requires std::is_same_v<R, int>
void foo(T, R); // #1
template <typename T, typename R>
void foo(T, R); // #2
Это сделает #1
"более ограниченным, чем" #2
, если оно жизнеспособно, поэтому было бы предпочтительным.