Check Variadic Templates Parameters for Uniqueness

Checking if variadic template parameters are unique using fold expressions

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

How to check if every type in a parameter pack is unique?

You could do it like this:

#include <type_traits>

template <class ... Trest>
struct unique_types;

template <class T1, class T2, class ... Trest>
struct unique_types<T1, T2, Trest ...>
: unique_types<T1, T2>, unique_types<T1, Trest ...>, unique_types<T2, Trest ...> {};

template <class T1, class T2>
struct unique_types<T1, T2>
{
static_assert(!std::is_same<T1, T2>::value, "Types must be unique");
};

int main()
{
// OK.
unique_types<int, double, char, float> foo;

// Should not compile.
unique_types<int, double, char, double> bar;
}

Find number of unique values of a parameter pack

Here's a simple O(n^2) way to do it

template <size_t...>
struct is_unique : std::integral_constant<bool, true> {};

template <size_t T, size_t U, size_t... VV>
struct is_unique<T, U, VV...> : std::integral_constant<bool, T != U && is_unique<T, VV...>::value> {};

template <size_t...>
struct no_unique : std::integral_constant<size_t, 0> {};

template <size_t T, size_t... UU>
struct no_unique<T, UU...> : std::integral_constant<size_t, is_unique<T, UU...>::value + no_unique<UU...>::value> {};

So using your example:

no_unique<0, 1, 2, 1, 2, 2>::value; // gives 3

How can I make sure a type only appear once in a template parameter?

For a simple compile time error if any type in the variadic parameter pack is duplicated, it's pretty simple:

template <typename T> struct Base{};
template <typename... Ts> struct NoDuplicates : Base<Ts>... {
constexpr operator bool() const { return true; }
};

That's it, and if it's what you need it will compile faster than any recursive template metaprogramming, fold expression, or type-trait approach. In fact, I know of no faster technique at compile time.
This works because a class is not allowed to inherit from the same base class twice. The reason it inherits from Base<T> instead of just T, is in case T is a type you can't inherit from, such as a primitive integral value, or an array, or void, etc.

To use:

template <typename... Ts>
class Foo {
static_assert(NoDuplicates<Ts...>{});
};

Foo<int, char, int> foo; // ERROR (see below)
<source>:3:34: error: duplicate base type 'Base<int>' invalid
3 | template <typename... Ts> struct NoDuplicates : Base<Ts>... {

Now, if you don't want a compile error, but want to compute a boolean indicating if there are any duplicates, it's a little more complicated, but not too bad. Comments show the 3 cases to check:

template <typename T, typename... Rest>
constexpr bool hasDuplicates() {
// Check T against each item in Rest, and if any match we have duplicates
if ((std::is_same_v<T, Rest> || ...))
return true;

// Is there anything left to check in Rest? If not, no duplicates.
if constexpr (sizeof...(Rest) == 0)
return false;

// T isn't duplicated, but is anything in Rest duplicated?
return hasDuplicates<Rest...>();
}

It can be used similarly:

template <typename... Ts>
class Foo {
static_assert(not hasDuplicates<Ts...>());
};

Foo<int, std::string, std::string> foo; // Error, there are dupes

And finally, if you only care if a specific type is duplicated, it is even easier:

// Check if Needle is found 2 or more times in this Haystack
template <typename Needle, typename... Haystack>
constexpr bool hasDuplicateInList() {
return ((std::is_same_v<Needle, Haystack> + ...)) > 1;
}

As far as "throwing" goes, if that's what you want, you can always throw an exception if you detect the boolean having a disallowed value in a normal if

Determine uniqueness of parameters at compile time

If you want to check the ts... values compile time (static_assert()) you have to pass they as values that are ever compile-time known.

You can't check compile-time the arguments for a constexpr constructors because the constructor can be used also with run-time values.

My suggestion is to pass the ts... values as template parameter, so they are ever known compile time, so you can check them in a static_assert() test.

By example: if Tp is an integer type, you can pass them as arguments of a std::integer_sequence

  template <Tp ... Ts>
constexpr Alphabet (std::integer_sequence<Tp, Ts...> const &)
: m_data{Ts...}
{ static_assert( sizeof...(Ts) <= Size
&& are_unique<Ts...>(), "!" ); }

Regarding are_unique(), I propose a recursive version

  // ground case
template <typename = void>
static constexpr bool are_unique ()
{ return true; }

// recursive case
template <Tp T0, Tp ... Ts>
static constexpr bool are_unique ()
{ return is_unique<T0, Ts...>() && are_unique<Ts...>(); }

and regarding is_unique(), you can use the trick of the initialization of an unused array

  template <Tp T0, Tp ... Ts>
static constexpr bool is_unique ()
{
using unused = bool[];

bool ret = true;

(void)unused{ false, ret &= T0 != Ts... };

return ret;
}

If you don't like, every time, to write the std::integer_sequence part, you can make it only one time in a make_Alphabet() template function

template <typename Tp, Tp ... Ts>
constexpr Alphabet<Tp, sizeof...(Ts)> make_Alphabet ()
{ return { std::integer_sequence<Tp, Ts...>{} }; }

and use it as follows

constexpr auto abcd{ make_Alphabet<char, 'a', 'b', 'c', 'd'>() };

The following is a full compiling C++14 example

#include <utility>
#include <iostream>

template <typename Tp, std::size_t Size>
class Alphabet
{
private:
Tp m_data[Size ? Size : 1U];

template <Tp T0, Tp ... Ts>
static constexpr bool is_unique ()
{
using unused = bool[];

bool ret = true;

(void)unused{ false, ret &= T0 != Ts... };

return ret;
}

// ground case
template <typename = void>
static constexpr bool are_unique ()
{ return true; }

// recursive case
template <Tp T0, Tp ... Ts>
static constexpr bool are_unique ()
{ return is_unique<T0, Ts...>() && are_unique<Ts...>(); }

public:
template <Tp ... Ts>
constexpr Alphabet (std::integer_sequence<Tp, Ts...> const &)
: m_data{Ts...}
{ static_assert( sizeof...(Ts) <= Size
&& are_unique<Ts...>(), "!" ); }
};

template <typename Tp, Tp ... Ts>
constexpr Alphabet<Tp, sizeof...(Ts)> make_Alphabet ()
{ return { std::integer_sequence<Tp, Ts...>{} }; }

int main()
{
// compilation error (static_assert failed "!")
//constexpr Alphabet<char, 3>
// aba{std::integer_sequence<char, 'a', 'b', 'a'>{}};

// compile
constexpr Alphabet<char, 3>
abc{std::integer_sequence<char, 'a', 'b', 'c'>{}};

// compilation error (static_assert failed "!")
//constexpr auto abca{ make_Alphabet<char, 'a', 'b', 'c', 'a'>() };

// compile
constexpr auto abcd{ make_Alphabet<char, 'a', 'b', 'c', 'd'>() };
}

Check if a type is passed in variadic template parameter pack

You're looking for std::disjunction. It's specified in N4564 [meta.logical].

#include <type_traits>

template<typename T, typename... Ts>
constexpr bool contains()
{ return std::disjunction_v<std::is_same<T, Ts>...>; }

static_assert( contains<int, bool, char, int, long>());
static_assert( contains<bool, bool, char, int, long>());
static_assert( contains<long, bool, char, int, long>());
static_assert(not contains<unsigned, bool, char, int, long>());

Live demo


Or, adapted to a struct

template<typename T, typename... Ts>
struct contains : std::disjunction<std::is_same<T, Ts>...>
{};

Or, using fold expressions

template<typename T, typename... Ts>
struct contains : std::bool_constant<(std::is_same<T, Ts>{} || ...)>
{};

Live demo

How can you statically check that a type T exists in a variadic template parameter list

Based on François' answer, here's a shorter version that avoids usage of std::tuple and uses std::integral_constant (via true/false_type) and provides a C++14-style contains_v alias. The general idea is the same though.

template <typename T, typename... Args>
struct contains;

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

template <typename T, typename... Args>
struct contains<T, T, Args...> : std::true_type {};

template <typename T, typename A, typename... Args>
struct contains<T, A, Args...> : contains<T, Args...> {};

template <typename T, typename... Args>
constexpr bool contains_v = contains<T, Args...>::value;

You can use it like this:

static_assert(contains_v<float, float, double>,
"failure: float not among <float, double>"); // does not trigger

static_assert(contains_v<int, float, double>,
"failure: int not among <float, double>"); // triggers


Related Topics



Leave a reply



Submit