Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking if variadic template parameters are unique using fold expressions

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?

like image 288
Joald Avatar asked Nov 27 '17 12:11

Joald


2 Answers

-- 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> );
 }
like image 99
max66 Avatar answered Sep 29 '22 12:09

max66


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.)

like image 30
Vittorio Romeo Avatar answered Sep 29 '22 13:09

Vittorio Romeo