Подтвердить что ты не робот

Неоднозначный вызов перегруженных шаблонов функций - даже если он более специализирован?

Рассмотрим следующее:

#include <utility>

template <int N>
using size_ = std::integral_constant<int, N>; 

template <int From>
void f(size_<From>, size_<From+1> ) // (1)
{ }

template <int From, int To>   // (2)
void f(size_<From>, size_<To> )
{ }

int main()
{
    f(size_<0>{}, size_<1>{});
}

Оба gcc и clang сообщают о вызове как неоднозначном. Зачем? Разве не (1) более специализированный, чем (2)?

Примечание. Я знаю, что это легко можно зафиксировать с добавлением дополнительного enable_if_t<(To > From+1)> в (2), но я бы не подумал, что мне нужно это сделать.

4b9b3361

Ответ 1

Это было - неудивительно - рассмотрено аналогичным примером в проблеме CWG, а именно # 455:

Если один из аргументов не выведен, то частичное упорядочение следует рассматривать только тип специализации:

template<typename T> struct B { typedef T type; };

template<typename T> char* f3(T, T);                   // #7
template<typename T> long* f3(T, typename B<T>::type); // #8

char* p3 = f3(p3, p3); // #9

В соответствии с моим рассуждением № 9 следует дать двусмысленность, поскольку вторая пара (T, long *). Второй тип (т.е. Длинный *) был взят из кандидат специальности №8. EDG и GCC приняли код. VC и BCC обнаружил двусмысленность.

И ICC и VС++ компилируют ваш код. Согласно текущей формулировке, они правильны: Каждая пара обрабатывается независимо, а как size_<From+1> делает From отображаться в невыводимом контексте ([temp.deduct.type]/(5.3)), дедукция обязательно терпит неудачу, и, следовательно, size_<From+1> по крайней мере так же специализирован, как size_<To>, но не наоборот. Таким образом, перегрузка (1) более специализирована, чем (2).

Итак, ICC и VС++ (предположительно) обрабатывают каждую пару дедукции и заключают, что для второго <<24 > не является, по крайней мере, таким же специализированным, как size_<From+1>.
Clang и GCC (предположительно) утверждают, что для (1), From выводится из первого аргумента и, следовательно, не нужно выводить во второй паре для size_<To> как личность, так и специализированную, как ее аналог.