11

Given a variadic template parameter pack, I want to check if all types given to it are unique using an inline constexpr bool and fold expressions. I trie something like this:

template<class... T>
inline static constexpr bool is_unique = (... && (!is_one_of<T, ...>));

Where is_one_of is a similar bool that works correctly. But this line doesn't compile regardless of what I put into is_one_of. Can this even be done using fold expressions, or do I need to use a regular struct for this purpose?

Community
  • 1
  • 1
Joald
  • 1,114
  • 10
  • 32

2 Answers2

12

You approach doesn't really work because is_one_of needs to be called with a type T and all the remaining types not including T. There's no way of expressing that with a fold expression over a single parameter pack. I suggest using specialization instead:

template <typename...>
inline constexpr auto is_unique = std::true_type{};

template <typename T, typename... Rest>
inline constexpr auto is_unique<T, Rest...> = std::bool_constant<
    (!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>
>{};   

Usage:

static_assert(is_unique<>);
static_assert(is_unique<int>);
static_assert(is_unique<int, float, double>);
static_assert(!is_unique<int, float, double, int>);

live example on wandbox.org


(Thanks to Barry for the simplification that uses a fold expression.)

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • You're right. I trusted my YouCompleteMe config too much, which surprisingly gave me diags even though it didn't even grok all the c++17 features. Disregard. I'll have to wrap my head around why it works, though because I suspected the given case to fail, from brain-compiling it. Got it; My brain-compiler failed to see `T1, Rest...` as "just the tail", which it of course is. +1 – sehe Nov 27 '17 at 13:05
  • 3
    Could simplify to `T0, Rest...` with the body of `(!is_same_v && ...) && is_unique`? – Barry Nov 27 '17 at 16:27
  • 2
    While the other answer provides a way to do that with fold expression and constexpr bool as I wanted, it comes with a lot of warnings and still requires additional structs which I wanted to do without, so your answer "You can't do it" is more appropriate. Thanks! – Joald Nov 27 '17 at 19:21
2

-- EDIT --

googling I've found an interesting solution that give me inspiration to avoid recursion and to avoid a lot of warnings

So you can define a wrapper of type

template <typename>
struct wrapT
 { };

and a wrapper for type and integer that inherit from the wrapper for type

template <typename T, std::size_t>
struct wrapTI : public wrapT<T>
 { };

Next you can define a foo class that recursively inherit from wrapTI

template <typename T,
          typename = std::make_index_sequence<std::tuple_size<T>::value>>
struct foo;

template <typename ... Ts, std::size_t ... Is>
struct foo<std::tuple<Ts...>, std::index_sequence<Is...>>
   : public wrapTI<Ts, Is>...
 { };

Now is_unique can be something like

template <typename ... Ts>
static constexpr bool isUnique
   = ( ... && std::is_convertible<foo<std::tuple<Ts...>>, wrapT<Ts>>::value );

The point is that foo<Ts...> can be converted to wrapT<T> only if foo<Ts...> inherit one time (and only one time) from wrapT<T>, that is if T is present one time (and only one time) in Ts....

The following is a full compiling example

#include <tuple>
#include <type_traits>

template <typename>
struct wrapT
 { };

template <typename T, std::size_t>
struct wrapTI : public wrapT<T>
 { };

template <typename T,
          typename = std::make_index_sequence<std::tuple_size<T>::value>>
struct foo;

template <typename ... Ts, std::size_t ... Is>
struct foo<std::tuple<Ts...>, std::index_sequence<Is...>>
   : public wrapTI<Ts, Is>...
 { };

template <typename ... Ts>
static constexpr bool isUnique
   = ( ... && std::is_convertible<foo<std::tuple<Ts...>>, wrapT<Ts>>::value );

int main ()
 {
   static_assert( true == isUnique<int, long, long long> );
   static_assert( false == isUnique<int, long, long long, int> );
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • While your answer provides a way to do that with fold expression and constexpr bool as I wanted, it comes with a lot of warnings and still requires additional structs which I wanted to do without, so the other answer "You can't do it" is more appropriate. But thanks, cool to know that it can be done! – Joald Nov 27 '17 at 19:22
  • @Joald - I'm agree with you: the Vittorio's answer is better than mine (and also I find annoying those warnings) – max66 Nov 27 '17 at 21:08
  • 1
    @Joald - Just for fun... answer modified, recursion avoided and warnings removed :) - But added another wrapper struct :( – max66 Nov 27 '17 at 22:12
  • That is really good solution for checking uniqueness that I was looking for. Thanks. – PiotrNycz Dec 07 '21 at 11:17