check variadic templates parameters for uniqueness

variadic template template parameter
variadic template no arguments
forward variadic template arguments
variadic template concept
c++ variadic templates unpack
how variadic template works
variadic template constructor
variadic templates recursion

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;

}

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 , "!");
}

c++ - 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. Variadic Template is introduced in C++11. Variadic Template allows a function to take variable number of arguments of any type. Let’s understand by an example. Suppose we want to create a function log() that accepts variable number of arguments of any type and prints them on console i.e.

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;
}

C++ Core Guidelines: Rules for Variadic Templates, 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  Variadic templates One of the new features of C++11 is variadic templates . Finally, there's a way to write functions that take an arbitrary number of arguments in a type-safe way and have all the argument handling logic resolved at compile-time, rather than run-time.

template<typename ... _Types>
class unique_types;

template<typename _T1, typename _T2, typename ... _Tail>
class unique_types<_T1,_T2,_Tail...> :
    virtual public unique_types<_T1, _T2>
    , virtual public unique_types<_T1, _Tail ...>
    , virtual public unique_types<_T2, _Tail ...>
{
protected:
    using check_current = unique_types<_T1, _T2>;
    using check_first = unique_types<_T1, _Tail ...>;
    using check_second = unique_types<_T2, _Tail ...>;
public:
    constexpr static const bool value = check_current::value && check_first::value && check_second::value;
};

template<typename _T1, typename _T2>
class unique_types<_T1, _T2, _T2>
{
public:
    constexpr static const bool value = false;
};

template<typename _T1, typename ... _Tail>
class unique_types<_T1, _T1, _Tail ...>
{
public:
    constexpr static const bool value = false;
};

template<typename _T1, typename _T2>
class unique_types<_T1, _T2, _T1>
{
public:
    constexpr static const bool value = false;
};

template<typename _T1, typename _T2>
class unique_types<_T1,_T2>
{
public:
    constexpr static const bool value = true;
};

template<typename _T1>
class unique_types<_T1,_T1>
{
public:
    constexpr static const bool value = false;
};

template<typename _T1>
class unique_types<_T1>
{
public:
    constexpr static const bool value = true;
};

class A
{
public:
    A() = default;
};


inline void test()
{
    const bool unique = unique_types<int, short, float, A>::value;
    assert(unique == true);
    const bool unique2 = unique_types<int, A, short, float, A>::value;
    assert(unique2 == false);
    const bool unique3 = unique_types<A, int, short, float, A>::value;
    assert(unique3 == false);
    const bool unique4 = unique_types<int, short, A, float, A>::value;
    assert(unique4 == false);
    const bool unique5 = unique_types<int, short, float, A, A>::value;
    assert(unique5 == false);
    const bool unique6 = unique_types<int>::value;
    assert(unique6 == true);
    const bool unique7 = unique_types<int, int>::value;
    assert(unique7 == false);
    const bool unique8 = unique_types<int, int, char>::value;
    assert(unique8 == false);
    const bool unique9 = unique_types<int, char, int>::value;
    assert(unique9 == false);
    const bool unique10 = unique_types<char, int, int>::value;
    assert(unique10 == false);
    const bool unique11 = unique_types<int, int, A, char>::value;
    assert(unique11 == false);
    const bool unique12 = unique_types<int, A, char, int>::value;
    assert(unique12 == false);
    const bool unique13 = unique_types<A, char, int, int>::value;
    assert(unique13 == false);
}

[PDF] Class Templates and Variadic Templates, How to process arguments to a variadic template ??? You can already guess it. Fine. An rvalue (line 2) and an lvalue (line 3) pass my test. Official doxygen git repository. Contribute to doxygen/doxygen development by creating an account on GitHub.

Using C++17, you can use fold expressions. Especially with larger numbers of template parameters, this version can be orders of magnitude faster (and less memory hungry) than the other solutions presented here:

#include <type_traits>

template <typename T> 
struct Base{};

template <typename... Ts>
struct TypeSet : Base<Ts>...
{     
   template<typename T>
   constexpr auto operator+(Base<T>)
   {
      if constexpr (std::is_base_of_v<Base<T>, TypeSet>)
        return TypeSet{};
      else
        return TypeSet<Ts..., T>{};
   }

   constexpr auto size() const -> std::size_t
   {
      return sizeof...(Ts);
   }
};

template<typename... Ts>
constexpr auto are_unique() -> bool
{ 
   constexpr auto set = (TypeSet<>{} + ... + Base<Ts>{});
   return set.size() == sizeof...(Ts);
}

int main()
{
   static_assert(are_unique<int, float, char, char*>());
   static_assert(not are_unique<int, float, char, char>());
}

See https://godbolt.org/z/_ELpyJ

Homogeneous variadic function parameters, 2 Variadic Templates. 3 Template Usage & Error checking. 4 Type Traits Intro class for each set of unique template parameters;. ‚ since the template  To unpack a parameter pack, use a templated function taking one (or more) parameters explicitly, and the ‘rest’ of the parameters as a template parameter pack. Recurse towards a base case: call your recursive function with the ‘rest…’ arguments, and let the compiler unpack your parameters in subsequent calls to your recursive function.

Test if parameter pack arguments are unique · GitHub, Added feature test macro. This paper seeks to expand the usefulness of variadic templates by The term homogeneous function parameter pack refers to a The 879 unique hits in boost tend to follow a generic pattern; nearly half (​416 hits) come from type_traits/detail/is_mem_fun_ptr_tester.hpp . parameters and arguments: class templates: function templates: class member templates: variable templates (C++14) template argument deduction: class template argument deduction (C++17) explicit (full) specialization: partial specialization: dependent names: parameter packs (C++11)

Generic message system using variadic templates, struct empty{};. template<typename Ts>. struct unique;. template<>. struct unique<>. {. static constexpr bool value = true;. }; template<typename T, typename. A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates). A function parameter pack is a function parameter that accepts zero or more function arguments. A template with at least one parameter pack is called a variadic template .

Advanced Template Use, hash_code() is not guaranteed to be unique. You are using hash_code() to check whether the type of a parameter pack is the same as that of a Message . But isn’t a variadic pack supposed to be the last parameter in a template parameters list? Can there be a default parameter after it? It turns out there can be, as long as the parameters in the pack are deduced, which is our case here. Indeed, they are deduced thanks to the function parameters. Could we do SFINAE on each parameter?

Comments
  • So, what you're saying is that both b and c should be true, right? Yet, they're both false. I think the real question is what's causing SFINAE if not multiple inheritance?
  • @jrok: that part seems to be down to not being able to convert 0 to a pointer. Somehow 0 doesn't want to be converted to base_all<T...>* although I haven't quite figured out why that is.
  • @Dietmar Kuhl. Thanks for answer, but I know recursive solution, but it needed deep recursions, and specializations. Try to test with 500 elements, you should feel difference )).
  • N.B. I'm surprised that the std::array trick is accepted by g++4.8.1 and clang++3.4. It doesn't look like an immediate context to me.