两个元组的编译时集减法
Compile-time set subtraction of two tuples
我有一个应用程序,我在其中从参数包构造元组:
template<class... T>
struct foo {
typedef std::tuple<T...> A;
}
我还有另一个元组类型 B 定义为:
typedef std::tuple<char, int> B;
有没有办法获得一个新的元组 C
,这样 C
是集合 A
和 B
中类型的集合减法 编译时?
A = {int, double, float, bool}
B = {char, int}
C = A - B = {double, float, bool} // answer
完整问题的更多上下文:
template<class... T>
struct foo {
using first_type = typename std::tuple_element<0, // n'th type
std::tuple<T...>>::type;
}
我可以通过上面的方法找到第0种,但我对第一种没有包含在上面的集合B中感兴趣。因此,有点像编译时搜索以查找第一个有效类型。
免责声明。请不要将这样的代码放入实际的代码库中。如果你这样做,这个实现绝不是“最佳”的,应该在使用前清理(如果你这样做,可能会添加一个答案)。
我发现这是一个有趣的挑战,并找到了以下解决方案:
// Try this live at https://compiler-explorer.com/z/cqebd81ss
#include <type_traits>
template <typename... Pack>
struct ClassList;
template<typename...>
struct Join {
};
template<typename... Pack1, typename... Pack2>
struct Join<ClassList<Pack1...>, ClassList<Pack2...>> {
using Type = ClassList<Pack1..., Pack2...>;
};
template<typename...>
struct RemoveSingleTypeFromList {
};
template<typename Target, typename... Pack>
struct RemoveSingleTypeFromList<Target, ClassList<Pack...>> {
using Type = ClassList<Pack...>;
};
template<typename Target, typename Parameter, typename... Pack>
struct RemoveSingleTypeFromList<Target, ClassList<Parameter, Pack...>> {
using Type = typename Join<
std::conditional_t<
std::is_same_v<Target, Parameter>,
ClassList<>,
ClassList<Parameter>
>,
typename RemoveSingleTypeFromList<Target, ClassList<Pack...>>::Type
>::Type;
};
template<typename... Pack>
struct RemoveTypesFromList {
};
template<typename... Types>
struct RemoveTypesFromList<ClassList<>, ClassList<Types...>> {
using Type = ClassList<Types...>;
};
template<typename Target, typename... RemainingTargets, typename... Types>
struct RemoveTypesFromList<ClassList<Target, RemainingTargets...>, ClassList<Types...>> {
using Type = typename RemoveSingleTypeFromList<
Target,
typename RemoveTypesFromList<
ClassList<RemainingTargets...>,
ClassList<Types...>
>::Type
>::Type;
};
// A few test cases to verify that it works
static_assert(std::is_same_v<
typename RemoveTypesFromList<
ClassList<int, float>,
ClassList<float, double, int, long>
>::Type,
ClassList<double, long>>);
static_assert(std::is_same_v<
typename RemoveTypesFromList<
ClassList<float>,
ClassList<float, double, float>
>::Type,
ClassList<double>>);
static_assert(std::is_same_v<
typename RemoveTypesFromList<
ClassList<int, int>,
ClassList<float, double, float>
>::Type,
ClassList<float, double, float>>);
如果您有兴趣了解它的确切工作原理,请记住这是一个解决方案,而不是您可以从上到下编写的东西。在阅读这样的模板代码时,我发现逐步构建类似的实现最有用。
这里是一个编译时集减法,作为set_diff<tuple1, tuple2>::type
:
template <class...> class set_diff;
template <class... A, class... B>
struct set_diff<std::tuple<A...>, std::tuple<B...>>
{
template <class J, class... Js>
constexpr static bool exists_in = std::disjunction_v<std::is_same<J, Js>...>;
using type = decltype(tuple_cat(std::declval<
std::conditional_t<exists_in<A, B...>, std::tuple<>, std::tuple<A>>>()...));
};
注意:
- 操作数的顺序很重要(根据问题描述)
- 有多种可能的编译复杂性“优化”(例如,在 diff 之前对类型进行排序和删除重复项等)。
我的解决方案分为两步:
- 将“不需要的”类型标记为
tuple<>
- 使用
tuple_cat
生成所需的 type
,驱逐空元组
可以在此处找到更不言自明(可惜更冗长)的演示
Demo
我有一个应用程序,我在其中从参数包构造元组:
template<class... T>
struct foo {
typedef std::tuple<T...> A;
}
我还有另一个元组类型 B 定义为:
typedef std::tuple<char, int> B;
有没有办法获得一个新的元组 C
,这样 C
是集合 A
和 B
中类型的集合减法 编译时?
A = {int, double, float, bool}
B = {char, int}
C = A - B = {double, float, bool} // answer
完整问题的更多上下文:
template<class... T>
struct foo {
using first_type = typename std::tuple_element<0, // n'th type
std::tuple<T...>>::type;
}
我可以通过上面的方法找到第0种,但我对第一种没有包含在上面的集合B中感兴趣。因此,有点像编译时搜索以查找第一个有效类型。
免责声明。请不要将这样的代码放入实际的代码库中。如果你这样做,这个实现绝不是“最佳”的,应该在使用前清理(如果你这样做,可能会添加一个答案)。
我发现这是一个有趣的挑战,并找到了以下解决方案:
// Try this live at https://compiler-explorer.com/z/cqebd81ss
#include <type_traits>
template <typename... Pack>
struct ClassList;
template<typename...>
struct Join {
};
template<typename... Pack1, typename... Pack2>
struct Join<ClassList<Pack1...>, ClassList<Pack2...>> {
using Type = ClassList<Pack1..., Pack2...>;
};
template<typename...>
struct RemoveSingleTypeFromList {
};
template<typename Target, typename... Pack>
struct RemoveSingleTypeFromList<Target, ClassList<Pack...>> {
using Type = ClassList<Pack...>;
};
template<typename Target, typename Parameter, typename... Pack>
struct RemoveSingleTypeFromList<Target, ClassList<Parameter, Pack...>> {
using Type = typename Join<
std::conditional_t<
std::is_same_v<Target, Parameter>,
ClassList<>,
ClassList<Parameter>
>,
typename RemoveSingleTypeFromList<Target, ClassList<Pack...>>::Type
>::Type;
};
template<typename... Pack>
struct RemoveTypesFromList {
};
template<typename... Types>
struct RemoveTypesFromList<ClassList<>, ClassList<Types...>> {
using Type = ClassList<Types...>;
};
template<typename Target, typename... RemainingTargets, typename... Types>
struct RemoveTypesFromList<ClassList<Target, RemainingTargets...>, ClassList<Types...>> {
using Type = typename RemoveSingleTypeFromList<
Target,
typename RemoveTypesFromList<
ClassList<RemainingTargets...>,
ClassList<Types...>
>::Type
>::Type;
};
// A few test cases to verify that it works
static_assert(std::is_same_v<
typename RemoveTypesFromList<
ClassList<int, float>,
ClassList<float, double, int, long>
>::Type,
ClassList<double, long>>);
static_assert(std::is_same_v<
typename RemoveTypesFromList<
ClassList<float>,
ClassList<float, double, float>
>::Type,
ClassList<double>>);
static_assert(std::is_same_v<
typename RemoveTypesFromList<
ClassList<int, int>,
ClassList<float, double, float>
>::Type,
ClassList<float, double, float>>);
如果您有兴趣了解它的确切工作原理,请记住这是一个解决方案,而不是您可以从上到下编写的东西。在阅读这样的模板代码时,我发现逐步构建类似的实现最有用。
这里是一个编译时集减法,作为set_diff<tuple1, tuple2>::type
:
template <class...> class set_diff;
template <class... A, class... B>
struct set_diff<std::tuple<A...>, std::tuple<B...>>
{
template <class J, class... Js>
constexpr static bool exists_in = std::disjunction_v<std::is_same<J, Js>...>;
using type = decltype(tuple_cat(std::declval<
std::conditional_t<exists_in<A, B...>, std::tuple<>, std::tuple<A>>>()...));
};
注意:
- 操作数的顺序很重要(根据问题描述)
- 有多种可能的编译复杂性“优化”(例如,在 diff 之前对类型进行排序和删除重复项等)。
我的解决方案分为两步:
- 将“不需要的”类型标记为
tuple<>
- 使用
tuple_cat
生成所需的type
,驱逐空元组
可以在此处找到更不言自明(可惜更冗长)的演示 Demo