【问题标题】:Group adjacent tuple elements matching a predicate into sub-tuples将匹配谓词的相邻元组元素分组为子元组
【发布时间】:2020-06-22 21:45:53
【问题描述】:

给定一个包含一些元素和一个谓词的元组,我如何将与谓词匹配的相邻元素组合成一个子元组?

一个例子:

#include <tuple>

auto is_float = [](auto x) {
    return std::is_same<decltype(x), float>{};
};

int main() {

    auto tup = std::tuple{1.f, 2.f, 3, 4, 5.f, 6.f, 7};
    auto result = magic_foo(is_float, tup); 
    // all adjacent floats are grouped into sub-tuples like this:
    auto expected = std::tuple{std::tuple{1.f, 2.f}, 3, 4, std::tuple{5.f, 6.f}, 7};
    assert(tup == expected);

}

【问题讨论】:

  • 谓词是否只依赖于类型?
  • 是的,仅在类型上,或者更准确地说是在类型的某些类型特征上

标签: c++ tuples c++17 metaprogramming


【解决方案1】:

一个非常粗略的实现:

#include <type_traits>
#include <tuple>
#include <typeinfo>
#include <iostream>

template <typename ... T1, typename ... T2, std::size_t ... I1, std::size_t ... I2>
auto concat_tuple(const std::tuple<T1...>& t1, const std::tuple<T2...>& t2, std::index_sequence<I1...>, std::index_sequence<I2...>) {
    return std::tuple<T1..., T2...>(
        std::get<I1>(t1)...,
        std::get<I2>(t2)...
    );
}

template <typename ... T1, typename ... T2>
auto concat_tuple(const std::tuple<T1...>& t1, const std::tuple<T2...>& t2) {
    return concat_tuple(t1, t2, 
        std::make_index_sequence<sizeof...(T1)>{},
        std::make_index_sequence<sizeof...(T2)>{}
    );
}

template <typename ... T, std::size_t ... I>
auto tuple_rest(const std::tuple<T...>& tup, std::index_sequence<I...>) {
    return std::make_tuple(std::get<I+1>(tup)...);
}

template <typename ... T>
auto tuple_rest(const std::tuple<T...>& tup) {
    return tuple_rest(tup, std::make_index_sequence<sizeof...(T) - 1>{});
}

template <template <typename T> class Pred, typename ... TAcc>
auto group_by_pred(std::tuple<TAcc...> acc, std::tuple<> tup) {
    if constexpr (sizeof...(TAcc) == 0u) {
        return std::make_tuple();
    }
    else return std::make_tuple(acc);
}

template <template <typename T> class Pred, typename ... TAcc, typename T0, typename ... T>
auto group_by_pred(std::tuple<TAcc...> acc, std::tuple<T0, T...> tup) {
    if constexpr (Pred<T0>::value) {
        return group_by_pred<Pred>(
            concat_tuple(acc, std::make_tuple(std::get<0>(tup))),
            tuple_rest(tup)
        );
    }
    else {
        if constexpr (sizeof...(TAcc) == 0u) {
            return concat_tuple(
                std::make_tuple(), 
                concat_tuple(
                    std::make_tuple(std::get<0>(tup)), 
                    group_by_pred<Pred>(std::make_tuple(), tuple_rest(tup))
                )
            );
        }
        else return concat_tuple(
            std::make_tuple(acc), 
            concat_tuple(
                std::make_tuple(std::get<0>(tup)), 
                group_by_pred<Pred>(std::make_tuple(), tuple_rest(tup))
            )
        );
    }
}

template <typename T>
struct not_int : std::true_type {};

template <>
struct not_int<int> : std::false_type {};

int main() {
    auto tup = std::make_tuple(1, 2.0, 3.0, 2, 3.0);
    auto result = group_by_pred<not_int>(std::make_tuple(), tup);
    std::cout << typeid(result).name() << "\n";
}

这个想法很简单,但是有很多模板样板。

Compiler Explorer Link

【讨论】:

  • 酷,非常感谢!一些 cmets:1) 你可以使用 std::tuple_cat 来减少 biolerplate,2) 不幸的是 MSVC 不喜欢它 (godbolt.org/z/fVmw4a),但你可以用 TAcc 替换 std::tuple&lt;TAcc...&gt; 并改用 std::tuple_sizesizeof...,然后它工作(见这里godbolt.org/z/NvQZB8。3)不幸的是,与gcc或clang相比,msvc将生成非常低效的代码。有什么想法可以改进吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-15
  • 2023-03-07
  • 1970-01-01
  • 2022-07-07
  • 2021-08-18
  • 1970-01-01
  • 2015-03-09
相关资源
最近更新 更多