【问题标题】:check variadic templates parameters for uniqueness检查可变参数模板参数的唯一性
【发布时间】:2013-09-29 23:04:24
【问题描述】:

我希望可变参数模板参数必须是唯一的。 我知道什么时候多继承,相同的类继承是不允许的。

struct A{};
struct B: A, A{}; // error

使用这个规则,我做了一点代码。

#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.
}

但我的程序没有按预期运行。怎么了?

//更新: //谢谢,我修复了我的错误: //

//     #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.
//        }
//

问:谁能解释一下,为什么会失败?

UPDATE2:我之前的更新是非法的 :))。合法的形式,但它编译了 O(N) 时间。

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

}

【问题讨论】:

    标签: c++ c++11 variadic-templates typetraits


    【解决方案1】:

    传递指向base_all&lt;U...&gt; 的指针只需要存在base_all&lt;U...&gt; 的声明。如果不尝试访问定义,编译器将不会检测到该类型实际上是错误定义的。缓解该问题的一种方法是使用需要定义base_all&lt;U...&gt; 的参数,例如:

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

    虽然上面回答了这个问题,但它无法编译:创建的多重继承不在 SFINAE 考虑的合适上下文中。我认为您不能利用不允许从两次继承的相同基数的规则。不过,相关测试可以采用不同的方式实现:

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

    【讨论】:

    • 那么,你的意思是bc 都应该是true,对吧?然而,他们都是false。我认为真正的问题是如果不是多重继承,是什么导致了 SFINAE?
    • @jrok:这部分似乎是因为无法将0 转换为指针。不知何故,0 不想转换为 base_all&lt;T...&gt;*,尽管我还没有完全弄清楚为什么会这样。
    • @Dietmar Kuhl。感谢您的回答,但我知道递归解决方案,但它需要深度递归和专业化。尝试用 500 个元素进行测试,你应该会感觉到不同))。
    【解决方案2】:

    另一个 O(logN) 实例化深度解决方案。它仍然需要进行重大清理、cmets、命名空间、重命名和减少代码重复。

    再次向 Xeo 致敬,O(logN) instantiation depth version of gen_seq 这个(再次)依赖于他。

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

    除了索引序列生成之外,这个解决方案甚至应该具有 O(1) 的实例化深度。它不是多重继承,而是使用std::array&lt;std::false_type, size&gt; 通过 SFINAE 执行 O(1)-instantiation-depth OR。

    is_one_of 的实现。请注意,“is one 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; }
    };
    

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

    使用示例:

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

    【讨论】:

    • 注:我很惊讶std::array 技巧被 g++4.8.1 和 clang++3.4 接受。对我来说,它看起来不像是直接的上下文。
    【解决方案3】:

    使用 C++17,您可以使用折叠表达式。尤其是在模板参数数量较多的情况下,此版本的速度可能比此处介绍的其他解决方案快几个数量级(并且内存占用更少):

    #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>());
    }
    

    https://godbolt.org/z/_ELpyJ

    【讨论】:

      【解决方案4】:

      如果您有一个 C++17 编译器并且您的类型列表不是太大,则可以使用折叠表达式在 4 行代码中完成。不过,算法本身具有二次复杂度。

      template<typename T, typename... Types>
      constexpr bool are_types_unique_v = (!std::is_same_v<T, Types> && ...) && are_types_unique_v<Types...>;
      template<typename T>
      constexpr bool are_types_unique_v<T> = true;
      

      【讨论】:

        【解决方案5】:
        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);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-05-30
          • 2016-12-01
          • 1970-01-01
          • 2015-03-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多