Reputation: 4860
Given this type:
template<typename ...As>
struct Base {};
I need to implement a function
template<int i, typename ...As>
constexpr auto Tail() {
static_assert(i < sizeof...(As), "index out of range");
return ??;
}
which returns an instance of B
using the tail of type parameter list from index i
.
For example,
Tail<0, int, float, double>() -> Base<int, float, double>
Tail<1, int, float, double>() -> Base<float, double>
Tail<2, int, float, double>() -> Base<double>
Tail<3, int, float, double>() -> fails with static assert
I know how to get the type at index i
:
template <int64_t i, typename T, typename... Ts>
struct type_at
{
static_assert(i < sizeof...(Ts) + 1, "index out of range");
typedef typename type_at<i - 1, Ts...>::type type;
};
template <typename T, typename... Ts> struct type_at<0, T, Ts...> {
typedef T type;
};
but I can't get a working version for the entire problem.
Upvotes: 5
Views: 298
Reputation: 66210
And now, for something completely different...
Just for fun I propose a C++14 solution that doesn't use recursion but the power of std::tuple_cat()
.
#include <tuple>
#include <type_traits>
template <typename...>
struct Base
{ };
template <std::size_t I, std::size_t J, typename A,
std::enable_if_t<(I <= J), bool> = true>
constexpr std::tuple<Base<A>> Tail_helper3 ();
template <std::size_t I, std::size_t J, typename A,
std::enable_if_t<(I > J), bool> = true>
constexpr std::tuple<> Tail_helper3 ();
template <typename ... As>
constexpr Base<As...> Tail_helper2 (std::tuple<Base<As>...> const &);
template <std::size_t I, typename ... As, std::size_t ... Is>
constexpr auto Tail_helper1 (std::index_sequence<Is...> const &)
-> decltype( Tail_helper2(std::tuple_cat(Tail_helper3<I, Is, As>()...)) );
template <std::size_t I, typename ... As>
constexpr auto Tail ()
-> decltype( Tail_helper1<I, As...>(std::index_sequence_for<As...>{}) )
{
static_assert(I < sizeof...(As), "index out of range");
return {};
}
int main ()
{
static_assert( std::is_same_v<Base<int, double, int>,
decltype(Tail<0u, int, double, int>())> );
static_assert( std::is_same_v<Base<double, int>,
decltype(Tail<1u, int, double, int>())> );
static_assert( std::is_same_v<Base<int>,
decltype(Tail<2u, int, double, int>())> );
// Tail<3u, int, double, int>(); compilation error!
}
Upvotes: 2
Reputation: 10315
If you don't mind using C++17. Even static_assert
is not needed:
template<unsigned i, typename T, typename ...Ts>
constexpr auto Tail() {
if constexpr (i == 0)
return Base<T, Ts...>();
else
return Tail<i - 1, Ts...>();
}
int main(){
Base<int, double, int> a = Tail<0, int, double, int>();
Base<double, int> b = Tail<1, int, double, int>();
Base<int> c = Tail<2, int, double, int>();
auto d = Tail<3, int, double, int>();
}
Btw, changed int
to unsigned
to avoid possibility of (nearly) infinite recursion for negative numbers.
Upvotes: 4
Reputation: 19607
For Tail
(why is it a function?), the approach can be almost the same as that of type_at
. The only thing you have to change is the base case of the recursion:
template <typename ... Ts>
struct types_from<0, Ts...> {
using type = Base<Ts...>; // we don't like nasty typedef syntax
};
Upvotes: 4