Question
Best concept to check that all types in a parameter pack are unique
I am trying to find the most efficient and clear way to define a C++20 concept to check that all types in a parameter pack are unique.
So far I have managed to do it like this (code excerpt):
template <size_t N, typename... Ts>
// recovers N-th typename from a parameter pack
using get_Nth_type = typename std::tuple_element_t<N, std::tuple<Ts...>>;
template <size_t N, typename... Ts>
// recovers N-th value of a corresponding type from a parameter pack
constexpr get_Nth_type<N, Ts...> get_Nth_value(Ts&&... values) { return std::get<N>(std::forward_as_tuple(std::forward<Ts>(values)...)); }
template<typename... Ts>
// concept is valid when all types in a parameter pack are the same
concept all_same = (std::same_as<get_Nth_type<0, Ts...>, Ts> and ...);
static_assert(all_same<int, float, float>); // fail
static_assert(all_same<float, float, float>); // OK
template <typename T, typename... Ts>
// concept is valid when a type T is among types in a parameter pack Ts
concept any_of = (std::same_as<T, Ts> or ...);
template<size_t N, typename... Ts, int... Js>
// compile-time boolean is true when N-th type in a parameter pack is equal to any other type in the pack
constexpr bool any_same_as_Nth_helper(std::integer_sequence<int, Js...>) { return ((Js != N && std::same_as<get_Nth_type<N, Ts...>, Ts>) or ...); }
template<size_t N, typename... Ts>
// concept is valid when N-th type in a parameter pack is equal to any other type in the pack
concept any_same_as_Nth = (any_same_as_Nth_helper<N, Ts...>(std::make_integer_sequence<int, sizeof...(Ts)>()));
static_assert(any_same_as_Nth<0, int, float, float>); // fail
static_assert(any_same_as_Nth<1, int, float, float>); // OK
template<typename... Ts, int... Is>
// compile-time boolean is true is valid when any type in a parameter pack Ts contains a duplicate
constexpr bool any_same_helper(std::integer_sequence<int, Is...>) { return ((any_same_as_Nth<Is, Ts...>) or ...); }
template<typename... Ts>
// concept is valid when all types in a parameter pack are unique
concept all_unique = (not any_same_helper<Ts...>(std::make_integer_sequence<int, sizeof...(Ts)>()));
static_assert(all_unique<int, float, int>); // fail
static_assert(all_unique<float, float, int>); // fail
static_assert(all_unique<int, float, double>); // OK
This works, but I am not sure about its efficiency for large packs, since it tests every possible pair from the parameter pack (with O(N^2) complexity).
Could anybody show me how to implement the same concept with, e.g., O(N log(N)) complexity - maybe using a binary tree search?