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
Why Is It Allowed to Pass R-Values by Const Reference But Not by Normal Reference
Initialize Parent's Protected Members with Initialization List (C++)
How to Use Std::Async Without Waiting for the Future Limitation
Call to Pure Virtual Function from Base Class Constructor
Structure of Arrays VS Array of Structures
How to Parse Date/Time from String
How to Run a Child Process That Requires Elevation and Wait
Win32 Programming Hiding Console Window
Difference Between Shared Objects (.So), Static Libraries (.A), and Dll's (.So)
How to Declare a Global Variable in C++
C/C++ Header and Implementation Files: How Do They Work