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?