【问题标题】:Filter a tuple of types in c++17在 c++17 中过滤类型的元组
【发布时间】:2018-11-30 22:49:00
【问题描述】:

std::tuple a{1,3,4,5} -> 使其成为大于 3 的数字

std::tuple b{4,5}    

或者

std::tuple a{
    std::integral_constant<int,1> {},
    std::integral_constant<int,3> {},
    std::integral_constant<int,4> {},
    std::integral_constant<int,5> {} 
}

std::tuple a{
    std::integral_constant<int,4>{},
    std::integral_constant<int,5>{}
};

如何在编译时转换它?我可以使用integer_sequence 来做到这一点,但这很麻烦。在 C++17 中是否有更简单的方法使用折叠表达式或std::apply

同样经过过滤,还需要得到一个唯一条目的元组。但我的假设是,如果可以进行过滤,那么找到唯一性将是微不足道的。

编辑以便更清楚: std::tuple&lt;int_c&lt;1&gt;, int_c&lt;3&gt;,int_c&lt;4&gt;,int_c&lt;5&gt;&gt; to std::tuple&lt;int_c&lt;4&gt;,int_c&lt;5&gt;

编辑: 我在摆弄,也许这样的东西会起作用:

template... C 作为积分常量列表:

constexpr auto result = std::tuple_cat(std::conditional_t<(C::value > 3), std::tuple<C>, std::tuple<>>{}...);

【问题讨论】:

  • 结果类型应该在编译类型时知道,你不能根据值这样做。
  • 如果结果是std::vector&lt;int&gt;是可能的。
  • 但您可以将template &lt;std::size_t N&gt;using int_c = std::integral_constant&lt;int, N&gt;; std::tuple&lt;int_c&lt;1&gt;, int_c&lt;3&gt;,int_c&lt;4&gt;,int_c&lt;5&gt;&gt; 转换为std::tuple&lt;int_c&lt;4&gt;,int_c&lt;5&gt;&gt;
  • @Jarod42 如果这些值实际上是包装类型,那不可能吗?
  • ` std::tuple, int_c,int_c,int_c> 到 std::tuple,int_c`

标签: c++ templates tuples c++17


【解决方案1】:

您可以使用 C++17 中的新 STL 实用程序来做到这一点。应该是这样的:

template<typename T>
auto filter(T tup) {
    return std::apply([&](auto first, auto... rest) {
        auto filtered_rest = [&]{
            if constexpr (sizeof...(rest)) {
                return filter(std::tuple{rest...});
            } else {
                return std::tuple{};
            }
        }();

        if constexpr (first > 3) {
            return std::tuple_cat(std::tuple{first}, filtered_rest);
        } else {
            return filtered_rest;
        }
    }, tup);
}

当然,还有很多其他方法可以做到这一点。在这种情况下,我使用了std::apply 和递归。我从一个空元组开始,一次添加一个元素。

现场示例:https://godbolt.org/z/qo63r4

【讨论】:

  • 不会编译,即使两个 lambda 的 2 返回类型不同。
  • @zoujyjs 除了缺少捕获之外,它确实可以编译。再次检查您的规则。
【解决方案2】:

用 c++17 生成你的tuple_cat

constexpr auto result = std::apply([](auto...ts) {
    return std::tuple_cat(std::conditional_t<(decltype(ts)::value > 3),
                          std::tuple<decltype(ts)>,
                          std::tuple<>>{}...);
}, tup);

【讨论】:

  • 这为简洁的解决方案提供了正确的方向,但此处的代码无法编译。我用auto... ts替换了第一个Ts...,用decltype(ts)替换了另一个Ts:然后它就完美地工作了。
  • 如果输入是std::tuple a{1,3,4,5},这种方法依赖于将值编码为可用作模板参数的类型integral_constant。 int 无论如何都没有关系,但是当元组元素是 constexpr 结构时,使用integral_constant 是冗长的。
  • @zoujyjs:返回类型不能依赖运行时值。您可以使用 template&lt;auto... Values&gt; using tupleValue = std::tuple&lt;std::integral_constant&lt;decltype(Values), Values&gt;...&gt;; 之类的东西来减少冗长(所以 tupleValue&lt;1, 3, 4, 5, '*', 1ULL&gt;)。
【解决方案3】:

一种可能的解决方案是生成一个特征,该特征将为所需元素输出std::tuple&lt;T&gt;,为不需要的元素输出std::tuple&lt;&gt;,并使用std::tuple_cat 将这些元组重新组合成一个类型。例如:

#include <tuple>
#include <type_traits>
#include <utility>

template <typename Pred, typename Tuple> struct filter;

template <typename t_Predicate, typename ...Ts> 
struct filter<t_Predicate, std::tuple<Ts...>>
{
    // If this element has to be kept, returns `std::tuple<Ts>`
    // Otherwise returns `std::tuple<>`
    template<class E>
    using t_filter_impl = std::conditional_t<
        t_Predicate<E>::value,
        std::tuple<E>, std::tuple<>>;

    // Determines the type that would be returned by `std::tuple_cat`
    //  if it were called with instances of the types reported by 
    //  t_filter_impl for each element
    using type = decltype(std::tuple_cat(std::declval<t_filter_impl<Ts>>()...));
};

其中t_Predicate&lt;T&gt; 是任何具有bool value; 成员的谓词类型,该成员确定T 是否是理想类型。例如,将此解决方案应用于原始问题,首先编写一个专门用于std::integral_constant 的谓词类型:

// Non integral_constant are not kept
template<class T>
struct four_or_more : std::integral_constant<bool, false> {};

// integral_const types are kept if their value is >=4
template<class T, T V>
struct four_or_more<std::integral_constant<T, V>> :
    std::integral_constant<bool, V >= 4> {};

这是一个演示:

#include <iostream>

int main()
{
    auto a = std::make_tuple(
        std::integral_constant<int,1> {},
        std::integral_constant<int,3> {},
        std::integral_constant<int,4> {},
        std::integral_constant<int,5> {}
    );

    using b_type = filter<four_or_more, decltype(a)>::type;

    std::cout << "size : " << std::tuple_size<b_type>() << std::endl;
    std::cout << std::tuple_element_t<0, b_type>::value << std::endl;
    std::cout << std::tuple_element_t<1, b_type>::value << std::endl;
}

【讨论】:

  • 如果您使用&gt;= 4 来避免模板的结尾&gt; 出现问题,请注意您可以使用括号&lt;bool, (V &gt; 3)&gt;
猜你喜欢
  • 1970-01-01
  • 2019-09-20
  • 2020-03-17
  • 2023-03-29
  • 1970-01-01
  • 2011-08-22
  • 1970-01-01
  • 1970-01-01
  • 2019-10-11
相关资源
最近更新 更多