【问题标题】:Check if one set of types is a subset of the other检查一组类型是否是另一组类型的子集
【发布时间】:2017-07-23 16:31:33
【问题描述】:

如何检查一个参数包(解释为一组)是否是另一个参数包的子集?

到目前为止,我只有框架(使用 std::tuple),但没有功能。

#include <tuple>
#include <type_traits>

template <typename, typename>
struct is_subset_of : std::false_type
{
};

template <typename ... Types1, typename ... Types2>
struct is_subset_of<std::tuple<Types1...>, std::tuple<Types2...>>
    : std::true_type
{
    // Should only be true_type if Types1 is a subset of Types2
};

int main() {
    using t1 = std::tuple<int, double>;
    using t2 = std::tuple<double, int>;
    using t3 = std::tuple<int, double, char>;

    static_assert(is_subset_of<t1, t1>::value, "err");
    static_assert(is_subset_of<t1, t2>::value, "err");
    static_assert(is_subset_of<t2, t1>::value, "err");
    static_assert(is_subset_of<t2, t3>::value, "err");
    static_assert(!is_subset_of<t3, t2>::value, "err");
}

每个类型都不允许在一个集合中出现多次。

如果该解决方案适用于 C++11,那就太好了。

【问题讨论】:

  • 我之前有一个similar question,但现在我无法将我的代码从相等检查修改为子集检查。

标签: c++ c++11 templates variadic-templates template-meta-programming


【解决方案1】:
#include <tuple>
#include <type_traits>

template <typename T, typename... Ts>
constexpr bool contains = (std::is_same<T, Ts>{} || ...);

template <typename Subset, typename Set>
constexpr bool is_subset_of = false;

template <typename... Ts, typename... Us>
constexpr bool is_subset_of<std::tuple<Ts...>, std::tuple<Us...>>
           = (contains<Ts, Us...> && ...);

DEMO

【讨论】:

  • 可能会补充一点,这只能从 C++17 开始工作。但这是迄今为止最优雅的解决方案!
  • 可以不用递归写containscontains = std::is_same&lt;T, Ts&gt;::value || ...; 反过来,它会让你在没有contains 的情况下写is_subset_of。但是,我认为我不能在没有递归或抽象的情况下编写“是子集的升序”。
  • @PiotrSkotnicki 我错了能够用一种表达方式做到这一点;据我所知,... 无法选择要扩展的包,这使得这不可能(可能是通过元函数之类的交叉产品?)
【解决方案2】:

如果你可以使用 C++17 特性,我强烈推荐使用Piotr Skotnicki's solution

前段时间我不得不实现这个功能。我只是复制粘贴我当时想出的代码。

我并不是说这是实施这种检查的最佳或最优雅的方式!我没有过多考虑边缘情况。您可能需要调整代码以满足您的要求。

澄清一下:ContainsTypes&lt;Lhs, Rhs&gt; 检查Rhs 是否是Lhs 的子集。


  template <typename Tuple, typename T>
  struct ContainsType;

  template <typename T, typename U, typename... Ts>
  struct ContainsType<std::tuple<T, Ts...>, U>
  {
      static const bool VALUE = ContainsType<std::tuple<Ts...>, U>::VALUE;
  };

  template <typename T, typename... Ts>
  struct ContainsType<std::tuple<T, Ts...>, T>
  {
      static const bool VALUE = true;
  };

  template <typename T>
  struct ContainsType<std::tuple<>, T>
  {
      static const bool VALUE = false;
  };

  // -----

  template <typename Lhs, typename Rhs>
  struct ContainsTypes;

  template <typename Tuple, typename T, typename... Ts>
  struct ContainsTypes<Tuple, std::tuple<T, Ts...>>
  {
      static const bool VALUE = ContainsType<Tuple, T>::VALUE && ContainsTypes<Tuple, std::tuple<Ts...>>::VALUE;
  };

  template <typename Tuple>
  struct ContainsTypes<Tuple, std::tuple<>>
  {
      static const bool VALUE = true;
  };

【讨论】:

  • 这正是我所需要的。谢谢。我modified it a bit 但只是为了更好地适应我已经拥有的东西。
  • @TobiasHermann 请原谅我懒惰地复制粘贴代码。我很高兴听到你让它与你的代码一起工作。还请务必查看 Piotr Skotnicki 的答案,因为我发现它是最优雅的解决方案,因为您可以访问 C++17 功能。
  • 仅当列表中没有类型出现多次时才有效。
  • @Oliv 好收获!我假设一个 set 不包含重复项(一个集合是不同对象的集合)。但是,这确实可能不符合您的需求。我还编写了一些结构来从 std::tuple 中删除重复类型,我在检查它们之前将其应用于两个参数。
  • @MaartenBamelis 没错,这完全是关于解释参数包的元素是什么?考虑到当您置换模板参数列表的类型时,两个模板不一定相等,我想说从模板的角度来看,参数包是从将 element_position 关联到 type 的类别的映射,其中 element_position 是有序集。所以,当我阅读参数包的一个子集时,虽然参数包不一定是一个集合,但我想这个问题承认了许多相互矛盾的答案。
【解决方案3】:

这是一个 C++17 的答案,我认为它比 Piotr's answer 简单得多:

template <class T, class... U>
struct contains : std::disjunction<std::is_same<T, U>...>{};

template <typename...>
struct is_subset_of : std::false_type{};

template <typename... Types1, typename ... Types2>
struct is_subset_of<std::tuple<Types1...>, std::tuple<Types2...>> : std::conjunction<contains<Types1, Types2...>...> {};

Demo

disjunctionconjunction 是 C++17 中引入的新类型特征。我们可以利用这些来检查第二个元组中的至少一个类型是否与第一个元组中的“下一个类型”匹配,我们广泛使用参数包扩展。

【讨论】:

    【解决方案4】:

    您可以使用类似以下的类来做到这一点:

    template<typename... Set>
    struct Check {
        template<typename Type>
        static constexpr bool verify() {
            using accumulator_type = bool[];
            bool check = false;
            accumulator_type accumulator = { (check = check || std::is_same<Type, Set>())... };
            (void)accumulator;
            return check;
        }
    
        template<typename... SubSet>
        static constexpr bool contain() {
            using accumulator_type = bool[];
            bool check = true;
            accumulator_type accumulator = { (check = check && verify<SubSet>())... };
            (void)accumulator;
            return check;
        }
    };
    

    在基于函数的示例中转换它很简单。
    它遵循适合您的代码的可能实现:

    #include <tuple>
    #include <type_traits>
    
    template<typename... Set>
    struct Check {
        template<typename Type>
        static constexpr bool verify() {
            using accumulator_type = bool[];
            bool check = false;
            accumulator_type accumulator = { (check = check || std::is_same<Type, Set>())... };
            (void)accumulator;
            return check;
        }
    
        template<typename... SubSet>
        static constexpr bool contain() {
            using accumulator_type = bool[];
            bool check = true;
            accumulator_type accumulator = { (check = check && verify<SubSet>())... };
            (void)accumulator;
            return check;
        }
    };
    
    
    template <typename, typename>
    struct is_subset_of;
    
    template <typename ... Types1, typename ... Types2>
    struct is_subset_of<std::tuple<Types1...>, std::tuple<Types2...>> {
        static constexpr bool value = Check<Types2...>::template contain<Types1...>();
    };
    
    int main() {
        using t1 = std::tuple<int, double>;
        using t2 = std::tuple<double, int>;
        using t3 = std::tuple<int, double, char>;
    
        static_assert(is_subset_of<t1, t1>::value, "err");
        static_assert(is_subset_of<t1, t2>::value, "err");
        static_assert(is_subset_of<t2, t1>::value, "err");
        static_assert(is_subset_of<t2, t3>::value, "err");
        static_assert(!is_subset_of<t3, t2>::value, "err");
    }
    

    这项工作在 Check 类及其方法 containverify 中完成。
    contain 成员函数是入口点。它使用一个常用技巧(在等待折叠表达式时)解包子集,并且需要对每个包含的类型进行显式检查。成员函数 verify 通过将单个类型与给定集合匹配来完成其余的工作。

    让我知道我是否可以给你更多的细节,或者它已经足够清楚了。


    查看它在 coliru 上运行。

    【讨论】:

      【解决方案5】:

      不完全是您所要求的,但...只是为了好玩,您可以使用 std::is_base_of 创建(至少在 C++14 中)一个像您的结构一样工作的 constexpr 函数。

      以下是一个工作示例(仅限 C++14)

      #include <tuple>
      #include <iostream>
      #include <type_traits>
      
      template <typename ... Ts>
      struct foo : std::tuple<Ts>...
       { };
      
      template <typename ... Ts1, typename ... Ts2>
      bool isSubsetOf (std::tuple<Ts1...> const &, std::tuple<Ts2...> const &)
       {
         bool ret { true };
      
         using un = int[];
         using d2 = foo<Ts2...>;
      
         (void)un { (ret &= std::is_base_of<std::tuple<Ts1>, d2>::value, 0)... };
      
         return ret;
       }
      
      
      int main()
       {
          using t1 = std::tuple<int, double>;
          using t2 = std::tuple<double, int>;
          using t3 = std::tuple<int, double, char>;
      
          std::cout << isSubsetOf(t1{}, t1{}) << std::endl;  // print 1
          std::cout << isSubsetOf(t1{}, t2{}) << std::endl;  // print 1
          std::cout << isSubsetOf(t2{}, t1{}) << std::endl;  // print 1
          std::cout << isSubsetOf(t1{}, t3{}) << std::endl;  // print 1
          std::cout << isSubsetOf(t3{}, t1{}) << std::endl;  // print 0
       }
      

      【讨论】:

        【解决方案6】:
        constexpr bool any_of() { return false; }
        template<class...Bools>
        constexpr bool any_of( bool b, Bools... bools ) {
          return b || any_of(bools...);
        }
        constexpr bool all_of() { return true; }
        template<class...Bools>
        constexpr bool all_of( bool b, Bools...bools ) {
          return b && all_of(bools...);
        }
        template<class T0, class...Ts>
        struct contains_t : std::integral_constant<bool,
          any_of( std::is_same<T0, Ts>::value... )
        > {};
        
        template<class Tuple0, class Tuple1>
        struct tuple_subset_of;
        
        template<class...T0s, class...T1s>
        struct tuple_subset_of< std::tuple<T0s...>, std::tuple<T1s...> >:
          std::integral_constant<bool,
            all_of( contains_t<T0s, T1s...>::value... )
          >
        {};
        

        Live example.

        这旨在允许 C++17 后的轻松改进 -- 用折叠表达式替换 any_ofall_of 递归实现。

        【讨论】:

          【解决方案7】:

          我想我会把我的帽子扔进擂台。这是 OP 要求的 C++11 解决方案,我意识到 C++17 具有更好的功能。这是一个仅限类型的解决方案(没有明确的static const bool 或类似的,只有true_typefalse_type,它们有自己的内部bool

          缺点是这个解决方案迫使我实现logical_orlogical_and,我们会以conjunctiondisjunction 的形式得到for free in C++17)。

          奇迹般地,代码比Maartan Barnelis' solution 短一点,但可读性可能会差一些

          namespace detail
          {
          template<class T, class U>
          struct logical_or : std::true_type{};
          
          template<>
          struct logical_or<std::false_type, std::false_type> : std::false_type{};
          
          template<class...>
          struct logical_and : std::false_type{};
          
          template<>
          struct logical_and<std::true_type, std::true_type> : std::true_type{};
          }
          
          template<class...>
          struct contains : std::false_type{};
          
          template<class T>
          struct contains<T, T> : std::true_type{};
          
          template<class Type, class Types2Head, class... Types2>
          struct contains<Type, Types2Head, Types2...> : detail::logical_or<typename std::is_same<Type, Types2Head>::type, typename contains<Type, Types2...>::type>{};
          
          template<class...>
          struct is_subset_of : std::false_type{};
          
          template<class Type1, class... Types2>
          struct is_subset_of<std::tuple<Type1>, std::tuple<Types2...>> : contains<Type1, Types2...>{};
          
          template<class Type1Head, class... Types1, class... Types2>
          struct is_subset_of<std::tuple<Type1Head, Types1...>, std::tuple<Types2...>> : detail::logical_and<typename contains<Type1Head, Types2...>::type, typename is_subset_of<std::tuple<Types1...>, std::tuple<Types2...>>::type>{};
          

          Demo

          【讨论】:

            【解决方案8】:

            一个(比前一个更)严肃的答案:使用 Skypjack 展示的技巧(谢谢!),您可以避免 ContainsTypeContainsTypes 的递归。

            以下是一个工作示例(不仅适用于std::tuples,还适用于通用(也不同)类型的容器)。

            #include <tuple>
            #include <type_traits>
            
            template <typename T, typename ... Ts>
            struct cType
            {
                static const bool value {
                    ! std::is_same<std::integer_sequence<bool,
                                      false, std::is_same<T, Ts>::value...>,
                                   std::integer_sequence<bool,
                                      std::is_same<T, Ts>::value..., false>>::value };
             };
            
            template <typename, typename>
            struct isSubsetOf : std::false_type
             { };
            
            template <template <typename...> class C1, template <typename...> class C2,
                      typename ... Ts1, typename ... Ts2>
            struct isSubsetOf<C1<Ts1...>, C2<Ts2...>>
                : std::integral_constant<bool,
                     std::is_same<std::integer_sequence<bool,
                                     true, cType<Ts1, Ts2...>::value...>,
                                  std::integer_sequence<bool,
                                     cType<Ts1, Ts2...>::value..., true>
                               >::value>
             { };
            
            
            int main()
            {
                using t1 = std::tuple<int, double>;
                using t2 = std::tuple<double, int>;
                using t3 = std::tuple<int, double, char>;
            
                static_assert(isSubsetOf<t1, t1>::value, "err");
                static_assert(isSubsetOf<t1, t2>::value, "err");
                static_assert(isSubsetOf<t2, t1>::value, "err");
                static_assert(isSubsetOf<t2, t3>::value, "err");
                static_assert(!isSubsetOf<t3, t2>::value, "err");
            }
            

            这个例子使用了std::integer_sequence,这是一个 C++14 的特性,但是创建一个 C++11 的替代品是微不足道的,比如:

            template <typename T, T ... ts>
            struct integerSequence
            { };
            

            【讨论】:

            • 不客气,但它是 skypjack,不是 Skypjack! :-)
            • @skypjack - 我有用大写字母开头的坏习惯。对不起:-(
            【解决方案9】:

            is_subset_of 您上一个问题的答案版本:

            #include <tuple>
            #include <type_traits>
            
            template <class T>
            struct tag { };
            
            template <class... Ts>
            struct is_subset_of_helper: tag<Ts>... { };
            
            template <class, class, class = void>
            struct is_subset_of: std::false_type { };
            
            template <bool...>
            struct bool_pack { };
            
            template <bool... Bs>
            using my_and = std::is_same<bool_pack<Bs..., true>, bool_pack<true, Bs...>>;
            
            template <class... Ts1, class... Ts2>
            struct is_subset_of<std::tuple<Ts1...>, std::tuple<Ts2...>, typename std::enable_if< my_and< std::is_base_of<tag<Ts1>, is_subset_of_helper<Ts2...>>::value...  >::value  >::type  >:
               std::true_type { };
            
            int main() {    
                using t1 = std::tuple<int, double>;
                using t2 = std::tuple<double, int>;
                using t3 = std::tuple<int, double, char>;
            
                static_assert(is_subset_of<t1, t1>::value, "err");
                static_assert(is_subset_of<t1, t2>::value, "err");
                static_assert(is_subset_of<t2, t1>::value, "err");
                static_assert(is_subset_of<t2, t3>::value, "err");
                static_assert(!is_subset_of<t3, t2>::value, "err");
            }
            

            [live demo]

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-04-11
              • 1970-01-01
              • 2010-09-24
              • 2010-10-21
              • 1970-01-01
              • 2016-11-24
              • 2012-05-20
              相关资源
              最近更新 更多