c++ - propio - Compruebe si un conjunto de tipos es un subconjunto del otro
subconjunto propio (9)
Aquí hay una respuesta de C ++ 17 que creo que es bastante más simple que la respuesta de Piotr :
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
disjunction
y conjunction
son nuevos rasgos de tipo introducidos en C ++ 17. Podemos aprovechar estos para verificar si al menos un tipo en la segunda tupla coincide con "el siguiente tipo" en la primera tupla, para lo cual usamos la expansión del paquete de parámetros ampliamente.
¿Cómo se puede verificar si un paquete de parámetros (interpretado como un conjunto) es un subconjunto de otro?
Hasta ahora solo tengo el marco (usando std :: tuple), pero ninguna funcionalidad.
#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");
}
No se permite que cada tipo ocurra más de una vez en un conjunto.
Sería bueno si la solución funciona con C ++ 11.
No es exactamente lo que pidió, pero ... solo por diversión, utilizando std::is_base_of
puede crear (en C ++ 14, al menos) una función constexpr
que funcione como su estructura.
El siguiente es un ejemplo de trabajo (solo 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
}
Puedes hacerlo con una clase como la siguiente:
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;
}
};
Convertirlo en un ejemplo basado en una función es sencillo.
Sigue una posible implementación adaptada a tu código:
#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");
}
El trabajo se realiza dentro de la clase Check
y sus métodos contain
y verify
.
La función miembro contenida es el punto de entrada. Utiliza un truco común (mientras espera las expresiones de plegado) para descomprimir el subconjunto y requiere una comprobación explícita de cada tipo contenido. La verify
función miembro hace el resto, haciendo coincidir el tipo único con el conjunto dado.
Déjame saber si puedo darte más detalles o es lo suficientemente claro como está.
Si puede usar las funciones de C ++ 17, ¡recomiendo usar la solución de Piotr Skotnicki !
Tuve que implementar esta funcionalidad hace un tiempo. Solo voy a copiar y pegar el código que se me ocurrió en ese momento.
¡No estoy diciendo que esta sea la mejor forma o la más elegante de implementar este tipo de verificación! No me molesté demasiado en pensar en los casos extremos; Es posible que necesite adaptar el código para que se ajuste a sus necesidades.
Para aclarar: ContainsTypes<Lhs, Rhs>
comprueba si Rhs
es un subconjunto de 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;
};
Supongo que voy a tirar mi sombrero en el ring. Esta es una solución de C ++ 11 como la solicitada por OP, me doy cuenta de que C ++ 17 tiene características mucho mejores. Es una solución de tipo único (no static const bool
explícita, true_type
ni similar, solo true_type
y false_type
, que tienen su propio bool
interno)
El inconveniente es que esta solución me obligó a implementar logical_or
y logical_and
, que obtendríamos conjunction en forma de conjunction
y disjunction
).
Milagrosamente, el código es un poco más corto que la solución de Maartan Barnelis , aunque posiblemente sea menos legible
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>{};
Una respuesta seria (un poco más) (que la anterior): utilizando un truco que mostró Skypjack (¡gracias!), Puede evitar la recursión de ContainsType
y ContainsTypes
.
El siguiente es un ejemplo funcional (que no solo funciona con std::tuple
s, sino también con contenedores de tipo genérico (también diferentes)).
#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");
}
Este ejemplo utiliza std::integer_sequence
que es una característica de C ++ 14, pero es trivial crear un sustituto de C ++ 11, como:
template <typename T, T ... ts>
struct integerSequence
{ };
is_subset_of
versión de una respuesta de tu pregunta anterior:
#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");
}
#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...> && ...);
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... )
>
{};
Esto está diseñado para permitir una mejora fácil después de C ++ 17: reemplace las implementaciones recursivas de any_of
y all_of
con expresiones de plegado.