首页 > 解决方案 > 在给定 constexpr 布尔选择函数的情况下对 Variadic 模板进行子集

问题描述

假设我们有一个可变参数模板类,如

template<class...Ts>
class X{
template<size_t I>
constexpr bool shouldSelect();

std::tuple<TransformedTs...> mResults; // this is want I want eventually
};

没有提供的实现shouldSelect,但它的作用是,给定一个i引用ivariadic 的第 th 元素的索引Ts,告诉您我们是否应该将它选择到子集。

我想进行转换,以便只选择导致返回 true的索引处的Tsclasses 。是否有捷径可寻?TshouldSelect

例如,如果当 I = 1,2,4 和 Ts... = short, int, double, T1, T2 时 shouldSelect 返回 true,那么我想获得TransformedTs...int, double, T2. 然后我可以TransformedTs...在同一个班级使用它。

标签: c++templates

解决方案


如果您能够使用 C++17,那么使用if constexpr表达式折叠的组合很容易实现。

从一些辅助类型开始,一种带有用于跟踪参数的参数X::shouldSelect<I>(),另一种带有要测试的类型。

template <typename T, size_t I, typename...Ts>
struct Accumulator {
    using Type = std::tuple<Ts...>;
};

template <typename T>
struct Next { };

然后运算符重载将类型添加到累加器,或者不使用if constexpr

template <typename TAcc, size_t I, typename... Ts, typename TArg>
decltype(auto) operator +(Accumulator<TAcc, I, Ts...>, Next<TArg>) {
    if constexpr (TAcc::template shouldSelect<I>()) {
        return Accumulator<TAcc, I + 1, Ts..., TArg>{};
    } else {
        return Accumulator<TAcc, I + 1, Ts...>{};
    }
}

最后,您可以将它们与折叠表达式放在一起,并使用 提取类型decltype

template <template <typename... Ts> class T, typename... Ts>
constexpr decltype(auto) FilterImpl(const T<Ts...>&) {
    return (Accumulator<T<Ts...>, 0>{} + ... + Next<Ts>{});
}

template<typename T>
using FilterT = typename decltype(FilterImpl(std::declval<T>()))::Type;

用法:

using Result = FilterT<X<int, double, bool, etc>>;

演示:https ://godbolt.org/z/9h89zG


如果您没有可用的 C++17,它仍然是可能的。您可以使用递归继承链执行相同类型的条件类型传输,以遍历参数包中的每种类型,并std::enable_if进行条件复制。下面是相同的代码,但在 C++11 中工作:

// Dummy type for copying parameter packs
template <typename... Ts>
struct Mule {};

/* Filter implementation */
template <typename T, typename Input, typename Output, size_t I, typename = void>
struct FilterImpl;

template <typename T, typename THead, typename... TTail, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<THead, TTail...>, Mule<OutputTs...>, I, typename std::enable_if<( T::template shouldSelect<I>() )>::type >
    : FilterImpl<T, Mule<TTail...>, Mule<OutputTs..., THead>, (I + 1)>
{ };

template <typename T, typename THead, typename... TTail, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<THead, TTail...>, Mule<OutputTs...>, I, typename std::enable_if<( !T::template shouldSelect<I>() )>::type >
    : FilterImpl<T, Mule<TTail...>, Mule<OutputTs...>, (I + 1)>
{ };

template <typename T, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<>, Mule<OutputTs...>, I>
{ 
    using Type = std::tuple<OutputTs...>;
};

/* Helper types */
template <typename T>
struct Filter;

template <template <typename... Ts> class T, typename... Ts>
struct Filter<T<Ts...>> : FilterImpl<T<Ts...>, Mule<Ts...>, Mule<>, 0>
{ };

template <typename T>
using FilterT = typename Filter<T>::Type;

演示:https ://godbolt.org/z/esso4M


推荐阅读