Определить кортеж рекурсивно - PullRequest
0 голосов
/ 25 сентября 2018

Я использую GCC8.2, и я хотел бы определить иерархический кортеж следующим образом:

std::tuple<std::array<double,N>,
           std::array<double,N/2>,
           std::array<double,N/4>,
           ...,
           std::array<double,2> > v ;

И затем у меня есть алгоритм для заполнения этих массивов со следующей спецификацией:

template <int N>
std::array<double,N> get_array()

Как мне написать общий алгоритм для объявления кортежа и его заполнения во время компиляции для любого N?

Ответы [ 3 ]

0 голосов
/ 25 сентября 2018

Без рекурсии вы можете сделать что-то вроде:

template <std::size_t N>
std::array<double, N> get_array() { return {{}}; }

namespace detail
{

    constexpr std::size_t log2(std::size_t n)
    {
        std::size_t res = 0;
        while (n != 0) {
            n /= 2;
            ++res;
        }
        return res;
    }

    template <std::size_t N, std::size_t ...Is>
    auto make_array_tuple_impl(std::index_sequence<Is...>)
    {
        return make_tuple(get_array<(N >> Is)>()...);
    }

}

template <std::size_t N>
auto make_array_tuple()
{
    return detail::make_array_tuple_impl<N>(std::make_index_sequence<detail::log2(N) - 1>());
}

Демо

0 голосов
/ 25 сентября 2018

Вы уверены, что ваша последовательность в

std::tuple<std::array<double,N>,
           std::array<double,N/2>,
           std::array<double,N/4>,
           ...,
           std::array<double,2> > v ;

заканчивается на 2?

Предположим, что вы хотите закончить на 2 или 1, я полагаю, вы можетеиспользуйте следующие пользовательские черты типа для получения требуемой последовательности индексов

template <std::size_t N, std::size_t ... Is>
struct divSequence : public divSequence<(N>>1u), Is..., (N>>1u)>
 { };

template <std::size_t ... Is>
struct divSequence<2u, Is...> : public std::index_sequence<Is...>
 { };

template <std::size_t ... Is>
struct divSequence<1u, Is...> : public std::index_sequence<Is...>
 { };

Таким образом, вам нужна только следующая функция make (с помощником)

template <std::size_t ... Is>
std::tuple<std::array<double, Is>...>
   getDivTupleHelper (std::index_sequence<Is...> const &)
 { return { get_array<Is>()... }; }

template <std::size_t N>
auto getDivTuple ()
 { return getDivTupleHelper(divSequence<N>{}); }

Ниже приведен полный компилятор C ++14 пример

#include <array>
#include <tuple>
#include <utility>

template <std::size_t N>
std::array<double,N> get_array ()
 { return {{ }}; }

template <std::size_t N, std::size_t ... Is>
struct divSequence : public divSequence<(N>>1u), Is..., (N>>1u)>
 { };

template <std::size_t ... Is>
struct divSequence<2u, Is...> : public std::index_sequence<Is...>
 { };

template <std::size_t ... Is>
struct divSequence<1u, Is...> : public std::index_sequence<Is...>
 { };

template <std::size_t ... Is>
std::tuple<std::array<double, Is>...>
   getDivTupleHelper (std::index_sequence<Is...> const &)
 { return { get_array<Is>()... }; }

template <std::size_t N>
auto getDivTuple ()
 { return getDivTupleHelper(divSequence<N>{}); }


int main ()
 {
   using t0 = decltype( getDivTuple<15u>() );
   using t1 = std::tuple<std::array<double, 7u>,
                         std::array<double, 3u>,
                         std::array<double, 1u>>;
   using t2 = decltype( getDivTuple<16u>() );
   using t3 = std::tuple<std::array<double, 8u>,
                         std::array<double, 4u>,
                         std::array<double, 2u>>;

   static_assert( std::is_same<t0, t1>::value, "!");
   static_assert( std::is_same<t2, t3>::value, "!");
 }

Если вам нужно решение C ++ 11 ... ну ... вместо std::index_sequence вы можете использовать пользовательскую тривиальную замену как

template <std::size_t ...>
struct myIndexSequence
 { }

и вам нужно переписать getDivTuple(), чтобы явно указать тип возвращаемого значения, используя decltype();что-то вроде

template <std::size_t N>
decltype(getDivTupleHelper(divSequence<N>{})) getDivTuple ()
 { return getDivTupleHelper(divSequence<N>{}); }
0 голосов
/ 25 сентября 2018

Это кажется немного сложным, может быть, есть более простое решение.

// Pack of values
template <auto...>
struct vpack { };

// Concatenating two packs
template <class, class>
struct vpack_cat_;

template <auto... lhs, auto... rhs>
struct vpack_cat_<vpack<lhs...>, vpack<rhs...>> {
    using type = vpack<lhs..., rhs...>;
};

template <class Lhs, class Rhs>
using vpack_cat = typename vpack_cat_<Lhs, Rhs>::type;

// Building a decreasing exp scale...
template <int N>
struct exp_scale_ {
    using type = vpack_cat<
        vpack<N>,
        typename exp_scale_<N / 2>::type
    >;
};

// ... stopping at 2
template <>
struct exp_scale_<2> {
    using type = vpack<2>;
};

template <int N>
using exp_scale = typename exp_scale_<N>::type;

// Building the tuple's type from the scale
template <class ScalePack>
struct exp_tuple_;

template <auto... Scale>
struct exp_tuple_<vpack<Scale...>> {
    using type = std::tuple<std::array<double, Scale>...>;
};

template <class Scale>
using exp_tuple = typename exp_tuple_<Scale>::type;

// The known get_array() function
template <int N>
std::array<double,N> get_array() { return {}; }

// Initializing the tuple
template <auto... Scale>
auto get_tuple(vpack<Scale...>) {
    return exp_tuple<vpack<Scale...>>{
        get_array<Scale>()...
    };
}

template <int N>
auto get_tuple() {
    return get_tuple(exp_scale<N>{});
}

Смотрите его в прямом эфире на Coliru

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...