Проверьте, является ли один набор типов подмножеством другого

Как проверить, является ли один пакет параметров (интерпретируемый как набор) подмножеством другого?

Пока у меня есть только фрейм (с использованием 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");
}

Каждому типу не разрешается встречаться более одного раза в наборе.

Было бы неплохо, если бы решение работало с С++ 11.

Ответы

Ответ 1

Если вы можете использовать возможности С++ 17, я настоятельно рекомендую использовать решение Piotr Skotnicki!

Мне пришлось реализовать эту функцию некоторое время назад. Я просто собираюсь скопировать код, который я придумал в этот момент.

Я не утверждаю, что это лучший или самый изящный способ реализовать этот вид проверки! Я не слишком беспокоился о крайних случаях; вам может потребоваться адаптировать код в соответствии с вашими требованиями.

Чтобы уточнить: ContainsTypes<Lhs, Rhs> проверяет, является ли 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;
  };

Ответ 2

#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

Ответ 3

Здесь ответ на С++ 17, который, я считаю, намного проще, чем Ответ Петра:

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

Демо

disjunction и conjunction являются новыми типами, введенными в С++ 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, и это методы contain и verify.
Функция-член contain является точкой входа. Он использует общий трюк (при ожидании выражений сгиба) для распаковки подмножества и требует явной проверки для каждого содержащегося типа. Функция-член verify делает остальные, сопоставляя один тип с заданным набором.

Сообщите мне, могу ли я дать вам более подробную информацию или это достаточно ясно, поскольку оно стоит.


Посмотрите, как работает coliru.

Ответ 5

Не совсем то, что вы просили, но... просто для удовольствия, используя std::is_base_of, вы можете создать (в С++ 14, по крайней мере) функцию constexpr, которая работает как ваша структура.

Ниже приведен рабочий пример (только С++ 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... )
  >
{};

Живой пример.

Это позволяет легко улучшать пост пост С++ 17 - заменить any_of и all_of рекурсивные реализации с помощью выражений fold.

Ответ 7

Полагаю, я брошу свою шляпу на ринг. Это решение на С++ 11, подобное запросам OP, я понимаю, что С++ 17 имеет гораздо более приятные функции. Это решение типа (без явного static const bool или тому подобного, только true_type и false_type, которые имеют свой собственный внутренний bool)

Недостатком является то, что это решение заставило меня реализовать logical_or и logical_and, которые мы получили бы бесплатно в С++ 17 в виде conjunction и disjunction).

Чудесно код немного короче решение Maartan Barnelis, хотя, возможно, менее читаемый

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

Демо

Ответ 8

A (немного больше) серьезный ответ (чем предыдущий): используя трюк, который показал Skypjack (спасибо!), вы можете избежать рекурсии для ContainsType и ContainsTypes.

Ниже приведен рабочий пример (который работает не только с std::tuple s, но и с универсальными (также разными) типами контейнеров).

#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, который является функцией С++ 14, но тривиально создать замену С++ 11, например:

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

Ответ 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]