检查variadic模板参数的唯一性

时间:2013-09-24 15:59:52

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

我希望variadic模板参数必须唯一。 我知道多继承时,不允许相同的类继承。

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;

}

5 个答案:

答案 0 :(得分:12)

将指针传递给base_all<U...>仅需要存在base_all<U...>的声明。在不尝试访问定义的情况下,编译器将不会检测到该类型实际上是错误定义的。缓解该问题的一种方法是使用需要定义base_all<U...>的参数,例如:

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

答案 1 :(得分:3)

另一个O(logN)实例化深度解决方案。它仍然需要一个主要的清理,注释,命名空间,重命名和减少代码重复。

再次感谢XeoO(logN) instantiation depth version of gen_seq这个(再次)依赖。{/ p>

#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<std::false_type, size>通过SFINAE执行O(1) - 实例化深度OR。

is_one_of的实施。请注意,“是其中之一”是“独特”的相反概念。

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

答案 2 :(得分:1)

使用C ++ 17,可以使用fold表达式。特别是在使用大量模板参数的情况下,该版本比此处提供的其他解决方案要快几个数量级(并且减少了内存消耗):

#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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

# GitLab lab setup
WEBLATE_GITLAB_USERNAME=weblate_user
WEBLATE_GITLAB_HOST=https://gitlab.my-company.com
WEBLATE_GITLAB_TOKEN=<MY_TOKEN>

# Authentication configuration
AUTHENTICATION_BACKENDS = (
    "social_core.backends.gitlab.GitLabOAuth2",
    "social_core.backends.email.EmailAuth",
    "weblate.accounts.auth.WeblateUserBackend",
)

# Social auth backends setup
SOCIAL_AUTH_GITLAB_KEY = "<MY_GITLAB_KEY>"
SOCIAL_AUTH_GITLAB_SECRET = "<MY_GITLAB_SECRET>"
SOCIAL_AUTH_GITLAB_SCOPE = ["read_user"]

# If you are using your own GitLab
SOCIAL_AUTH_GITLAB_API_URL = 'https://gitlab.my-company.com/'