I'd package them up in a variant.
First write:
template<class...Ts, class V=std::variant<std::decay_t<Ts>...>>
V pick(std::size_t i, Ts&&...ts );
that returns a variant with the ith argument held.
Then:
auto z = pick(some_condition?0:1, std::views::all(x), x | std::views::reverse);
Now your code runs via std::visit
.
std::visit( [&](auto&& elems){
for( auto&& elem: elems ) {
// loop
}
}, z );
pick
implementation:
namespace impl {
template<class...Ts, std::size_t...Is, class V=std::variant<std::decay_t<Ts>...>>
V pick(std::index_sequence<Is...>, std::size_t i, Ts&&...ts )
{
using pF = V(*)(std::tuple<Ts&&...>);
const pF pickers[] = {
+[](std::tuple<Ts&&...> t)->V{
return V( std::in_place_index<Is>, std::get<Is>(std::move(t)) );
}...
};
return pickers[i]( std::forward_as_tuple(std::forward<Ts>(ts)...) );
}
}
template<class...Ts, class V=std::variant<std::decay_t<Ts>...>>
V pick(std::size_t i, Ts&&...ts ) {
return impl::pick( std::make_index_sequence<sizeof...(Ts)>{}, i, std::forward<Ts>(ts)... );
}
and a lazy-evaluation variant:
namespace impl {
template<class...Fs, std::size_t...Is, class V=std::variant<std::invoke_result_t<Fs>...>>
V lazy_pick(std::index_sequence<Is...>, std::size_t i, Fs&&...fs )
{
using pF = V(*)(std::tuple<Fs&&...>);
const pF pickers[] = {
+[](std::tuple<Fs&&...> t)->V{
return V( std::in_place_index<Is>, std::get<Is>(std::move(t))() );
}...
};
return pickers[i]( std::forward_as_tuple(std::forward<Fs>(fs)...) );
}
}
template<class...Fs, class V=std::variant<std::invoke_result_t<Fs>...>>
V lazy_pick(std::size_t i, Fs&&...fs ) {
return impl::lazy_pick( std::make_index_sequence<sizeof...(Fs)>{}, i, std::forward<Fs>(fs)... );
}
Live example.