两个排版/元组中的常见类型

Common types in two TypeSets / Tuples

本文关键字:元组 常见 类型 两个      更新时间:2023-10-16

我有两个元组 - TypeSets建模为元组,因此保证在其参数包中最多包含每个类型一次,确切地说 - (比如A = std::tuple<T1, T2>B = std::tuple<T2, T3>),我希望获得一个类型def,它对应于AB交集中的类型元组(在这种情况下,tuple_intersect<A,B>::type = std::tuple<T2>)。我该怎么做?

您可以将索引技巧与has_type一起使用(从这里开始):

#include <tuple>
#include <type_traits>
// ##############################################
// from https://stackoverflow.com/a/25958302/678093
template <typename T, typename Tuple>
struct has_type;
template <typename T>
struct has_type<T, std::tuple<>> : std::false_type {};
template <typename T, typename U, typename... Ts>
struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {};
template <typename T, typename... Ts>
struct has_type<T, std::tuple<T, Ts...>> : std::true_type {};
// ##############################################

template <typename S1, typename S2>
struct intersect
{
template <std::size_t... Indices>
static constexpr auto make_intersection(std::index_sequence<Indices...> ) {
return std::tuple_cat(
std::conditional_t<
has_type<
std::tuple_element_t<Indices, S1>,
S2
>::value,
std::tuple<std::tuple_element_t<Indices, S1>>,
std::tuple<>
>{}...);
}
using type = decltype(make_intersection(std::make_index_sequence<std::tuple_size<S1>::value>{}));
};

struct T1{};
struct T2{};
struct T3{};
using A = std::tuple<T1, T2>;
using B = std::tuple<T2, T3>;
int main()
{
static_assert(std::is_same<std::tuple<T2>, intersect<A, B>::type>::value, "");
}

现场示例

这个问题分几个部分解决。

在第一部分中,让我们创建一个template<typename type_2_search, typename ...all_types> class type_search;,用于确定type_2_search是否是...all_types中的任何类型

#include <type_traits>
#include <iostream>
#include <tuple>
template<typename type_2_search, typename ...all_types> class type_search;
template<typename type_2_search,
typename type_2_compare,
typename ...all_types> class type_compare
: public type_search<type_2_search, all_types...>
{
};
template<typename type_2_search,
typename ...all_types>
class type_compare<type_2_search, type_2_search, all_types...>
: public std::true_type {};
template<typename type_2_search>
class type_search<type_2_search> : public std::false_type {};
template<typename type_2_search, typename first_type, typename ...all_types>
class type_search<type_2_search, first_type, all_types...> :
public type_compare<type_2_search, first_type, all_types...>
{
};
int main()
{
std::cout << type_search<int, char, double, int *>::value << std::endl;
std::cout << type_search<int, int, char, double, int *>::value << std::endl;
std::cout << type_search<int, char, double, int *, int>::value << std::endl;
std::cout << type_search<int, char, int, double, int *>::value << std::endl;
}

生成的输出为:

0
1
1
1

下一部分是template<typename type, bool value, typename tuple_bag> class add_2_bag_if_type_in_tuple;。第一个参数是类型。第三个参数是std::tuple<types...>。如果第二个booltrue,模板会给你一个std::tuple<type, types...>,它会添加元组的类型。否则,它会返回相同的元组。相当简单:

template<typename type, bool value, typename tuple_bag>
class add_2_bag_if_type_in_tuple;
template<typename type, typename tuple_bag>
class add_2_bag_if_type_in_tuple<type, false, tuple_bag> {
public:
typedef tuple_bag type_t;
};
template<typename type, typename ...types>
class add_2_bag_if_type_in_tuple<type, true, std::tuple<types...>> {
public:
typedef std::tuple<type, types...> type_t;
};

现在,我们在最后一部分中拥有创建tuple_intersection模板的所有缺失部分。我们迭代第一个元组的类型,使用第一个模板根据第二个元组中的类型检查每个类型,然后将结果传递给第二个模板。

首先,专业化,当我们到达第一个元组类型的末尾时:

template<typename tuple1_types,
typename tuple2_types> class compute_intersection;
template<typename ...tuple2_types>
class compute_intersection<std::tuple<>,
std::tuple<tuple2_types...>> {
public:
typedef std::tuple<> type_t;
};

对于拼图游戏的最后一部分:从第一个元组中拔出第一个类型,递归使用compute_intersection来计算第一个元组的其余部分与第二个元组的交集,然后type_search拔掉的类型,然后'add_2_bag_if_type_in_tuple:

template<typename tuple1_type,
typename ...tuple1_types, typename ...tuple2_types>
class compute_intersection<std::tuple<tuple1_type, tuple1_types...>,
std::tuple<tuple2_types...>> {
public:
typedef typename compute_intersection<std::tuple<tuple1_types...>,
std::tuple<tuple2_types...>>
::type_t previous_bag_t;
typedef typename add_2_bag_if_type_in_tuple<
tuple1_type,
type_search<tuple1_type, tuple2_types...>::value,
previous_bag_t>::type_t type_t;
};

完整的测试程序:

#include <type_traits>
#include <iostream>
#include <tuple>
template<typename type_2_search, typename ...all_types> class type_search;
template<typename type_2_search,
typename type_2_compare,
typename ...all_types> class type_compare
: public type_search<type_2_search, all_types...>
{
};
template<typename type_2_search,
typename ...all_types>
class type_compare<type_2_search, type_2_search, all_types...>
: public std::true_type {};
template<typename type_2_search>
class type_search<type_2_search> : public std::false_type {};
template<typename type_2_search, typename first_type, typename ...all_types>
class type_search<type_2_search, first_type, all_types...> :
public type_compare<type_2_search, first_type, all_types...>
{
};
// add_2_bag_if_type_in_tuple adds the type to tuple_bag
//
// The third template parameter is a tuple_bag
//
// If the 2nd template parameter is true, add the first parameter to the
// bag of types, otherwise the bag of types is unchanged.
template<typename type, bool value, typename tuple_bag>
class add_2_bag_if_type_in_tuple;
template<typename type, typename tuple_bag>
class add_2_bag_if_type_in_tuple<type, false, tuple_bag> {
public:
typedef tuple_bag type_t;
};
template<typename type, typename ...types>
class add_2_bag_if_type_in_tuple<type, true, std::tuple<types...>> {
public:
typedef std::tuple<type, types...> type_t;
};
/////////

template<typename tuple1_types,
typename tuple2_types> class compute_intersection;
template<typename ...tuple2_types>
class compute_intersection<std::tuple<>,
std::tuple<tuple2_types...>> {
public:
typedef std::tuple<> type_t;
};
template<typename tuple1_type,
typename ...tuple1_types, typename ...tuple2_types>
class compute_intersection<std::tuple<tuple1_type, tuple1_types...>,
std::tuple<tuple2_types...>> {
public:
typedef typename compute_intersection<std::tuple<tuple1_types...>,
std::tuple<tuple2_types...>>
::type_t previous_bag_t;
typedef typename add_2_bag_if_type_in_tuple<
tuple1_type,
type_search<tuple1_type, tuple2_types...>::value,
previous_bag_t>::type_t type_t;
};
int main()
{
// Test case: no intersection
typedef compute_intersection<std::tuple<int>, std::tuple<char>>::type_t
one_type;
std::tuple<> one=one_type();
// Test case: one of the types intersect
typedef compute_intersection<std::tuple<int, char>,
std::tuple<char, double>>::type_t
two_type;
std::tuple<char> two = two_type();
// Test case, two types intersect, but in different order:
typedef compute_intersection<std::tuple<int, char, int *>,
std::tuple<int *, char, double>>::type_t
three_type;
std::tuple<char, int *> three = three_type();
}

如果返回的一个类型不是默认可构造的,则来自 @m.s. 的答案不起作用。这是因为make_intersection尝试在获取返回类型之前创建生成的元组。

我们可以通过仅处理类型来避免这种情况:

#include <tuple>
#include <type_traits>
// ##############################################
// from https://stackoverflow.com/a/25958302/678093
// (c++17 version)
template <typename T, typename Tuple>
struct has_type;
template <typename T, typename... Us>
struct has_type<T, std::tuple<Us...>>
: std::disjunction<std::is_same<T, Us>...> {};
// ##############################################
template <typename... Ts>
using tuple_cat_t =
decltype(std::tuple_cat(std::declval<Ts>()...));
template <typename S1, typename S2> struct intersect {
template <typename>
struct build_intersection;
template <std::size_t... Indices>
struct build_intersection<std::index_sequence<Indices...>> {
using type = tuple_cat_t<
std::conditional_t<
has_type<std::tuple_element_t<Indices, S1>, S2>::value,
std::tuple<std::tuple_element_t<Indices, S1>>, std::tuple<>
>...>;
};
using type = typename build_intersection<
std::make_index_sequence<std::tuple_size<S1>::value>>::type;
};
struct T1{};
struct T2{
T2(int) {};
};
struct T3{};
using A = std::tuple<T1, T2>;
using B = std::tuple<T2, T3>;
int main()
{
static_assert(std::is_same<std::tuple<T2>, intersect<A, B>::type>::value, "");
}