4

如何获取参数包的前 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 的组合来实现,但我想知道是否有更简单的方法。

4

2 回答 2

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 来提高效率。

于 2020-10-13T23:41:23.510 回答
0

有一种方法是通过使用, ,的组合为其创建一个新的工厂别名std::integer_sequence作为构建块。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_sequence( make_consecutive_index_sequencefor std::size_t) 以便我们可以以一致的方式为headtail和编写一个简洁的实现。slice

编译时断言:

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"));
于 2021-07-18T08:42:22.817 回答