How to check uniqueness of types of template template parameter?

Viewed 116

I have the following code to check the uniqueness of template parameters:

template <typename ...Tn>
struct uniq_types 
{
    static constexpr const bool value = true;
};

template <typename T1, typename T2, typename ...Tn>
struct uniq_types<T1, T2, Tn...> 
{
    static constexpr const bool value
        = !std::is_same<T1, T2>::value && uniq_types<T1, Tn...>::value && uniq_types<T2, Tn...>::value;
};

template <typename T>
struct uniq_types<T> 
{
    static constexpr const bool value = true;
};

It works fine. But then, if I want to check the uniqueness of the template parameters of a container, std::tuple for example, I need to partially specialize it like:

template <typename ...Tn>
struct uniq_types<std::tuple<Tn...>> : public uniq_types<Tn...> 
{
};

I wonder whether there is a way to do so without partial specialization for every container.

I have tried

template <template <typename...> typename U, typename... Tn>
struct unique_types;

template <template <typename T1, typename T2, typename... Tn> typename U,
                    typename T1, typename T2, typename... Tn>
struct unique_types<U, T1, T2, Tn...> 
{
    static constexpr const bool value 
        = !std::is_same<T1, T2>::value && unique_types<U, T1, Tn...>::value && unique_types<U, T2, Tn...>::value;
};

template <template <typename T> typename U, typename T>
struct unique_types<U, T> 
{
    static constexpr const bool value = true;
};

It works, but then to check, I have to "expand" the template parameters of the container like this:

unique_types<std::tuple, int>::value

I would like to do something like this:

unique_types<std::tuple<int>>::value; // true
unique_types<std::tuple<int, double>>::value; // true
unique_types<std::tuple<int, double, int>>::value; // false
3 Answers

You can make the traits to specialise for the template template parameters. Then using std::conjunction you can do this

#include <type_traits> // std::is_same, std::conjunction_v

template<typename Type, typename... Rs>
constexpr bool areSameTypes = std::conjunction_v<std::is_same<Type, Rs>...>;

template <typename T>
struct unique_types : std::false_type {};

template <template<typename...> class Container, typename... V>
struct unique_types<Container<V...>>
{
   constexpr static bool value = areSameTypes<V...>;
};

Now you could

static_assert(unique_types<std::tuple<int>>::value, "not unique");
static_assert(unique_types<std::tuple<int, int, int>>::value, "not unique");
// static_assert(unique_types<std::tuple<int, char, int>>::value, "not unique");  // error

See a demo

I ended up with something like this

#include <tuple>
#include <type_traits>

template <typename ...Tn>
struct uniq_types 
{
    static constexpr const bool value = true;
};

template <typename T1, typename T2, typename ...Tn>
struct uniq_types<T1, T2, Tn...> 
{
    static constexpr const bool value
        = !std::is_same<T1, T2>::value && uniq_types<T1, Tn...>::value && uniq_types<T2, Tn...>::value;
};

template <typename T>
struct uniq_types<T> 
{
    static constexpr const bool value = true;
};

template <typename T>
struct unique_types : uniq_types<T> {
    static constexpr const bool value = uniq_types<T>::value;
};

template <template <typename...> typename C, typename... Tn>
struct unique_types<C<Tn...>> {
    static constexpr const bool value = uniq_types<Tn...>::value;
};

int main() {
    static_assert(unique_types<std::tuple<int>>::value); // okay
    static_assert(unique_types<std::tuple<int, bool, bool, char>>::value); // error
}
Related