check variadic templates parameters for uniqueness
I want variadic template parameters must unique. I know when multi inheritance, identical classes inheritance is not allowed.
struct A{};
struct B: A, A{}; // error
Using this rule, I made a little code.
#include <type_traits>
template< class T> struct id{};
template< class ...T> struct base_all : id<T> ... {};
template< class ... T>
struct is_unique
{
template< class ... U>
static constexpr bool test( base_all<U...> * ) noexcept { return true; }
template< class ... U>
static constexpr bool test( ... ) noexcept { return false;}
static constexpr bool value = test<T...>(0);
};
int main()
{
constexpr bool b = is_unique<int, float, double>::value; // false -- Why?
constexpr bool c = is_unique< int, char, int>::value; // false
static_assert( b == true && c == false , "!");// failed.
}
But my program is not worked as I expected. What's wrong?
//UPDATE: //Thanks, I fix my error: //
// #include <type_traits>
// #include <cstddef>
//
// template< class ... U> struct pack{};
//
// template< class T> struct id{};
// template< class T> struct base_all;
// template< class ... T> struct base_all< pack<T...> > : id<T> ... {};
//
//
//
// template< class ... T>
// struct is_unique
// {
// template< class P, std::size_t = sizeof(base_all<P>) >
// struct check;
//
// template< class ...U>
// static constexpr bool test(check< pack<U...> > * ) noexcept { return true;}
//
// template< class ... U>
// static constexpr bool test(...)noexcept { return false;}
//
// static constexpr bool value = test<T...>(0);
// };
//
// int main()
// {
// constexpr bool b = is_unique<int, float, double>::value; // true
// constexpr bool c = is_unique< int, char, int>::value; // false
//
// static_assert( b == true && c == false , "!");// success.
// }
//
Q: somebody can explain, why it's failed?
UPDATE2: My previous update was illegal :)). Legal form, but it compiled O(N) time.
#include <cstddef>
#include <iostream>
#include <type_traits>
namespace mpl
{
template< class T > using invoke = typename T :: type ;
template< class C, class I, class E > using if_t = invoke< std::conditional< C{}, I, E> >;
template< class T > struct id{};
struct empty{};
template< class A, class B > struct base : A, B {};
template< class B , class ... > struct is_unique_impl;
template< class B > struct is_unique_impl<B>: std::true_type{};
template< class B, class T, class ... U>
struct is_unique_impl<B, T, U...> : if_t< std::is_base_of< id<T>, B>, std::false_type, is_unique_impl< base<B,id<T>>, U...> >{};
template< class ...T >struct is_unique : is_unique_impl< empty, T ... > {};
} // mpl
int main()
{
constexpr bool b = mpl::is_unique<int, float, double>::value;
constexpr bool c = mpl::is_unique< int, char, int > :: value;
static_assert( b == true , "!");
static_assert( c == false, "!");
return 0;
}
Solution 1:
Passing a pointer to base_all<U...>
merely requires the existence of a declaration of base_all<U...>
. Without attempting the to access the definition, the compiler won't detect that the type is actually ill-defined. One approach to mitigate that problem would be to use an argument which requires a definition of base_all<U...>
, e.g.:
template< class ...T> struct base_all
: id<T> ...
{
typedef int type;
};
// ...
template< class ... U>
static constexpr bool test(typename base_all<U...>::type) noexcept
{
return true;
}
Although the above answers the question, it fail to compile: the multiple inheritance created isn't in a suitable context to be considered for SFINAE. I don't think you can leverage the rule on not allowing the same base inherited from twice. The relevant test can be implemented differently, though:
#include <type_traits>
template <typename...>
struct is_one_of;
template <typename F>
struct is_one_of<F>
{
static constexpr bool value = false;
};
template <typename F, typename S, typename... T>
struct is_one_of<F, S, T...>
{
static constexpr bool value = std::is_same<F, S>::value
|| is_one_of<F, T...>::value;
};
template <typename...>
struct is_unique;
template <>
struct is_unique<> {
static constexpr bool value = true;
};
template<typename F, typename... T>
struct is_unique<F, T...>
{
static constexpr bool value = is_unique<T...>::value
&& !is_one_of<F, T...>::value;
};
int main()
{
constexpr bool b = is_unique<int, float, double>::value;
constexpr bool c = is_unique< int, char, int>::value;
static_assert( b == true && c == false , "!");
}
Solution 2:
Another O(logN) instantiation depth solution. It still needs a major cleanup, comments, namespaces, renaming, and reduced code duplication.
Kudos again to Xeo, whose O(logN) instantiation depth version of gen_seq
this (again) relies upon.
#include <cstddef>
// using aliases for cleaner syntax
template<class T> using Invoke = typename T::type;
template<std::size_t...> struct seq{ using type = seq; };
template<class S1, class S2> struct concat;
template<std::size_t... I1, std::size_t... I2>
struct concat<seq<I1...>, seq<I2...>>
: seq<I1..., (sizeof...(I1)+I2)...>{};
template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;
template<std::size_t N> struct gen_seq;
template<std::size_t N> using GenSeq = Invoke<gen_seq<N>>;
template<std::size_t N>
struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};
template<> struct gen_seq<0> : seq<>{};
template<> struct gen_seq<1> : seq<0>{};
Aside from the indices sequence generation, this solution should even have O(1) instantiation depth. Instead of multiple inheritance, it uses a std::array<std::false_type, size>
to do a O(1)-instantiation-depth OR via SFINAE.
Implementation of is_one_of
. Note that "is one of" is the opposite concept of "are unique".
#include <array>
// check if `T` is in `Us...`
template<class T, class... Us>
struct is_one_of
{
template<class T1>
static constexpr auto SFINAE(int)
-> decltype( std::array<std::false_type, sizeof...(Us)>
{{std::is_same<T1, Us>{}...}} )
{ return {}; /* only to suppress warning */ }
template<class...>
static constexpr int SFINAE(...) { return 42; }
template<class T1>
static constexpr bool test()
{
return std::is_same<decltype(SFINAE<T1>(0)), int>{};
}
static constexpr bool value = test<T>();
constexpr operator bool() const { return value; }
};
Implementation of are_unique
:
namespace detail
{
// `Any` type with a generic no-constraint ctor
// to discard a number of arguments for a function template
template<std::size_t>
struct Any
{
template<class T>
constexpr Any(T&&) {}
};
// `wrapper` is used as a substitute for `declval`,
// and can keep track if `T` is a reference
template<class T>
struct wrapper { using type = T; };
template<std::size_t I, class T, class... Us>
struct is_one_of_pack
{
template<std::size_t... I1s>
struct helper
{
template<class... Remaining>
static constexpr bool deduce_remaining(Any<I1s>..., Remaining...)
{
// unique <-> is one of
return not is_one_of<T, typename Remaining::type...>{};
}
};
template<std::size_t... I1s>
static constexpr bool deduce_seq(seq<I1s...>)
{
return helper<I1s...>::template deduce_remaining(wrapper<Us>()...);
}
static constexpr bool create_seq()
{
return deduce_seq(gen_seq<I+1>{});
}
using type = std::integral_constant<bool, create_seq()>;
};
template<class... Packs>
constexpr auto SFINAE(int)
-> decltype( std::array<std::true_type, sizeof...(Packs)>
{{typename Packs::type{}...}} )
{ return {}; /* only to suppress warning */ }
template<class...>
static constexpr int SFINAE(...) { return 42; }
template<class... Packs>
constexpr bool test()
{
return std::is_same<decltype(SFINAE<Packs...>(0)), int>{};
}
template<class... Ts, std::size_t... Is>
constexpr bool deduce_seq(seq<Is...>)
{
return test< is_one_of_pack<Is, Ts, Ts...>... >();
}
}
template<class... Ts>
struct are_unique
: std::integral_constant<bool,
detail::deduce_seq<Ts...>(gen_seq<sizeof...(Ts)>{})>
{};
Usage example:
#include <iostream>
#include <iomanip>
int main()
{
bool a = are_unique<bool, char, int>();
bool b = are_unique<bool, char, int, bool>();
bool c = are_unique<bool, char, bool, int>();
std::cout << std::boolalpha;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
}