If you remove the default template arguments, it'll become clear what the difference is. Function declarations cannot differ in just their defaults. This is ill-formed:
void foo(int i = 4);
void foo(int i = 5);
And likewise this is ill-formed:
template <typename T=int> void foo();
template <typename T=double> void foo();
With that in mind, your first case:
template<typename T, is_ref<T>>
void foo(T&&);
template<typename T, is_not_ref<T>>
void foo(T&&);
The two declarations here are unique because the second template parameter differs between the two declarations. The first has a non-type template parameter whose type is std::enable_if_t<std::is_reference_v<T>, bool>
and the second has a non-type template parameter whose type is std::enable_if_t<!std::is_reference_v<T>, bool>
. Those are different types.
Whereas, your second case:
template<typename T, typename>
void foo(T&&);
template<typename T, typename>
void foo(T&&)
This is clearly the same signature - but we've just duplicated it. This is ill-formed.
See also cppreference's explanation.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…