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