Disclaimer. Please don't put code like this into an actual code base. If you do, this implementation is by no means "optimal" and should be cleaned up before use (maybe add an answer if you do).
I found this an interesting challenge, and found the following solution:
// 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>>);
If you are interested in understanding exactly how this works, remember that this is a solution and not something you could write top-to-bottom. When reading template code like this, I find it most useful to build a similar implementation step by step.