两个元组的编译时集合减法

问题描述

我有一个应用程序,我正在从参数包中构造元组

template<class... T>
struct foo {
 typedef std::tuple<T...> A;
}

我有一个元组类型 B 定义为:

typedef std::tuple<char,int> B;

有没有办法获得一个新的元组C,使得C中集合AB中的类型的集合减法编译时

A = {int,double,float,bool}
B = {char,int}
C = A - B = {double,bool} // answer

完整问题的更多背景:

template<class... T>
struct foo {
 using first_type = typename std::tuple_element<0,// n'th type 
                     std::tuple<T...>>::type;
}

我可以通过上面的方法找到第 0 种类型,但我对上面 B 组中未包含的第一种类型感兴趣。所以,有点像在编译时搜索找到第一个有效类型。

解决方法

这里是一个编译时集合减法,用作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>>>()...));
};

Demo

注意:

  • 操作数的顺序很重要(根据问题描述)
  • 可能对编译复杂性进行各种“优化”(例如,在差异之前对类型进行排序和删除重复项等)。

我的解决方案分为两步:

  1. 将“不需要的”类型标记为 tuple<>
  2. 使用 type 生成所需的 tuple_cat,这会驱逐空元组

可以在此处找到更不言自明(更详细)的演示 Demo

,

免责声明。请不要将这样的代码放入实际的代码库中。如果你这样做,这个实现绝不是“最佳”,应该在使用前清理(如果你这样做,可能会添加一个答案)。

我发现这是一个有趣的挑战,并找到了以下解决方案:

// 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,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>,float>
    >::Type,ClassList<double>>);

static_assert(std::is_same_v<
    typename RemoveTypesFromList<
        ClassList<int,int>,float>>);

如果您有兴趣确切了解其工作原理,请记住这是一个解决方案,而不是您可以从上到下编写的内容。在阅读这样的模板代码时,我发现逐步构建类似的实现最有用。