【问题标题】:C++: Get head and tail of a parameter packC++:获取参数包的头部和尾部
【发布时间】:2021-01-27 23:20:00
【问题描述】:

如何获取参数包的前n个元素?还是最后 n 个元素,或者 [n, n+1, ..., m) 中的元素切片?例如:

head<3>(1, 2.0f, "three", '4') => make_tuple(1, 2.0f, "three")
tail<2>(1, 2.0f, "three", '4') => make_tuple("three", '4')
slice<1,3>(1, 2.0f, "three", '4') => make_tuple(2.0, "three")

这可以通过 std::tuple、std::integer_sequence 和 std::get 的组合来实现,但我想知道是否有更简单的方法。

【问题讨论】:

    标签: c++ templates variadic parameter-pack


    【解决方案1】:
    
    template <typename T, typename... J>
    T GetFirstPack(T t, J... j) {
        return t;
    }
    
    template <typename T>
    T GetLastPack(T t) {
        return t;
    }
    template <typename T, typename...J>
    auto GetLastPack(T t, J... j) {
        return GetLastPack(j...);
    }
    
    template <typename... T>
    void TestFunction(T... t) {
        std::cout << GetFirstPack(t...) << std::endl;
        std::cout << GetLastPack(t...) << std::endl;
    }
    
    int main() {
        // your code goes here
        TestFunction("first", "second", 10);
        return 0;
    }
    

    首先是相当琐碎的,考虑到解包规则,最后一个有点棘手。您可以使用 rhr 来提高效率。

    【讨论】:

    • 感谢您的回复,但总体上似乎没有回答问题。
    【解决方案2】:

    有一种方法可以为 std::integer_sequence 创建一个新的 factory 别名,将 std::tuplestd::integer_sequencestd::get 的组合用作构建块。

    假设这个别名的名称是make_consecutive_integer_sequence

    namespace detail {
      template <typename T, auto Start, auto Step, T... Is>
      constexpr auto make_cons_helper_impl_(std::integer_sequence<T, Is...>) {
        auto eval_ = [](const T& I) consteval -> T { return Start + Step * I; };
        return std::integer_sequence<T, eval_(Is)...>{};
      }
    
      template <typename T, auto Start, auto Count, auto Step>
      constexpr auto make_cons_impl_() {
        return make_cons_helper_impl_<T, Start, Step>(std::make_integer_sequence<T, Count>{});
      }
    } // namespace detail
    
    template <std::integral T, auto Start, auto Count, auto Step = 1>
    using make_consecutive_integer_sequence = decltype(
      detail::make_cons_impl_<T, Start, Count, Step>()
    );
    
    template <auto Start, auto Count, auto Step = 1>
    using make_consecutive_index_sequence = make_consecutive_integer_sequence<std::size_t, Start, Count, Step>;
    

    然后,应用make_consecutive_integer_sequence的用法:

    template <std::size_t N>
    using make_first_n_index_sequence = make_consecutive_index_sequence<0, N>;
    
    template <std::size_t N, std::size_t S>
    using make_last_n_index_sequence = make_consecutive_index_sequence<S - N, N>;
    
    template <std::size_t B, std::size_t E>
    using make_slice_index_sequence = make_consecutive_index_sequence<B, E - B>;
    

    我们仍然需要将参数包包装std::tuple

    template <typename... Ts, std::size_t... Is>
    constexpr auto get_subpack_by_seq(std::index_sequence<Is...>, Ts&&... args) {
        return std::make_tuple(std::get<Is>(std::forward_as_tuple(args...))...);
    }
    
    template <std::size_t N, typename... Ts>
    requires (N <= sizeof...(Ts))
    constexpr auto head(Ts&&... args) {
        return get_subpack_by_seq(
            make_first_n_index_sequence<N>{},
            std::forward<Ts>(args)...
        );
    }
    
    template <std::size_t N, typename... Ts>
    requires (N <= sizeof...(Ts))
    constexpr auto tail(Ts&&... args) {
        return get_subpack_by_seq(
            make_last_n_index_sequence<N, sizeof...(Ts)>{},
            std::forward<Ts>(args)...
        );
    }
    
    template <std::size_t B, std::size_t E, typename... Ts>
    requires (B < E && B <= sizeof...(Ts) && E <= sizeof...(Ts))
    constexpr auto slice(Ts&&... args) {
        return get_subpack_by_seq(
            make_slice_index_sequence<B, E>{},
            std::forward<Ts>(args)...
        );
    }
    

    这里的目标是概括 make_consecutive_integer_sequencemake_consecutive_index_sequence 代表 std::size_t),以便我们可以以一致的方式为 headtailslice 编写一个简洁的实现。

    编译时断言:

    static_assert(head<3>(1, 2.0f, "three", '4') == std::make_tuple(1, 2.0f, "three"));
    static_assert(tail<2>(1, 2.0f, "three", '4') == std::make_tuple("three", '4'));
    static_assert(slice<1, 3>(1, 2.0f, "three", '4') == std::make_tuple(2.0f, "three"));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-17
      • 2021-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多