Взять за хвост параметры вариабельного шаблона - PullRequest
5 голосов
/ 28 мая 2019

Учитывая этот тип:

template<typename ...As>
struct Base {};

Мне нужно реализовать функцию

template<int i, typename ...As>
constexpr auto Tail() {
   static_assert(i < sizeof...(As), "index out of range");
   return ??;
}

, который возвращает экземпляр B, используя хвост списка параметров типа из индекса i.

Например,

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:

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;
};

но я не могу получить рабочую версию для всей проблемы.

Ответы [ 3 ]

4 голосов
/ 28 мая 2019

Если вы не возражаете против использования C ++ 17.Даже static_assert не требуется:

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>();
}

Кстати, изменено int на unsigned, чтобы избежать возможности (почти) бесконечной рекурсии для отрицательных чисел.

4 голосов
/ 28 мая 2019

Для Tail (почему это функция?) Подход может быть почти таким же, как у type_at. Единственное, что вам нужно изменить - это базовый вариант рекурсии:

template <typename ... Ts>
struct types_from<0, Ts...> {
    using type = Base<Ts...>; // we don't like nasty typedef syntax
};
2 голосов
/ 28 мая 2019

А теперь, для чего-то совершенно другого ...

Просто для удовольствия я предлагаю решение C ++ 14, которое не использует рекурсию, но обладает силой 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!
 }
...