Lets take something concrete:
#include <utility>
#include <vector>
template <typename ... Ts>
using void_t = void;
template <typename T, typename = void_t<>>
struct is_lt_comparable : std::false_type {};
template <typename T>
struct is_lt_comparable<T, void_t<decltype(std::declval<T>() < std::declval<T>())>> : std::true_type {};
template <typename T>
static constexpr bool is_lt_comparable_v = is_lt_comparable<T>::value;
struct test{};
int main()
{
#define error_message "detection doesn't work correctly"
static_assert(is_lt_comparable_v<int>, error_message);
static_assert(!is_lt_comparable_v<test>, error_message);
static_assert(is_lt_comparable_v<std::vector<int>>, error_message);
}
In the code above, why doesn't first and the last asserts trigger double definition of is_lt_comparable?
void_t with any arguments is still void. As such, the last unnamed parameter for the template is always void. IIRC type aliases are not considered distinct types, thus my intuition leads me to believe that I'm missing something.
Specifically, given a choice that both declaration are valid, and result in the same type, e.g. in the first one, is_lt_comparable<int, void>, how does it know which template to instantiate?