С++ 11 вариативное программирование, как определить башню векторов

Как (если возможно) я могу использовать вариативное программирование С++ 11 для определения серии vector в теле функции (или, другими словами, последовательности N-мерных массивов с уменьшением N до 0), как переменные ниже?

vector<vector<vector<int>>> v<3>;
vector<vector<int>> v<2>;
vector<int> v<1>;
int v<0>;

Я представлял себе что-то вроде:

#include <iostream>
#include <vector>
using namespace std;

template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };

template<int ...S>
void f(seq<S...>) {
  //how do I write definitions of v<N> here?
  vector<vector<...(N layers)<vector<int> ...> v<N>;     //??how-to, not valid c++
  vector<vector<...(N -1 layers)<vector<int> ...> v<N-1>;//??how-to, not valid c++
  //...
  vector<int> v<1>;  //??how-to, not valid c++
  int v<0>;          //??how-to, not valid c++

  //...
}

int main() {
  f(typename gens<3>);
  return 0;
}

Кроме того, будет ли это проще в С++ 14?

Спасибо,

--РЕДАКТИРОВАТЬ--

Просто чтобы уточнить, что я подразумеваю под «башней векторов», лучше всего описывает N-кортеж (v_1, v_2,..., v_N), где N — целочисленный параметр шаблона. v_1 — вектор, v_2 — вектор> и так далее.

--EDIT2--

На данный момент ответы quantdev и R успешно решили проблему определения N-кортежа для любого фиксированного N, например 3, но не могут создать кортеж для неопределенного N. В дополнение к функциональности в ответах мне нужна функция, которую можно использовать как gen_tower<N>, которая возвращает tuple(v1,v2,...,vN).

Рассмотрим пример использования вариативного программирования для вычисления факториалов. Мне нужна функция для вычисления факториала factorial<N>() для любого N, в дополнение к возможности выписывать любое конкретное выражение <1*2*3> вручную. (Вот почему я спросил о вариативном программировании и о том, упростит ли его c++14.)

P.S.

Исключительно из личных интересов я хочу, чтобы эта последовательность реализовывала универсальную функцию, которая может читать N-мерный массив из файла. Я пока точно не знаю, как это сделать, но думаю, что на первом этапе я смогу определить окончательный N-мерный массив и промежуточные k-мерные массивы для k от N-1 до 1. Я могу читать двумерные массивы и трехмерные. Но было бы неплохо иметь возможность читать массивы любой размерности.


person tinlyx    schedule 28.06.2014    source источник
comment
Можете ли вы проиллюстрировать, как вы собираетесь использовать эти N массивов, или это упражнение для их определения?   -  person Cheers and hth. - Alf    schedule 28.06.2014
comment
@ Альф, это исключительно из личных интересов. пожалуйста, смотрите обновление в P.S. ваше здоровье.   -  person tinlyx    schedule 28.06.2014


Ответы (3)


Нет необходимости в вариациях, рекурсивного typedef достаточно для генерации этих типов во время компиляции.


Как это реализовано?

1) Предоставьте шаблону 2 аргумента: тип векторных элементов (T) и требуемый размер структуры (size_t N). Объявите typedef type : он будет основан на объявлении type для шаблона, созданного с глубиной N-1, отсюда и рекурсия.

template<typename T, size_t N>
struct VectorGenerator
{
    typedef std::vector< typename VectorGenerator<T, N-1>::type > type;
};

2) Укажите случай завершения для завершения рекурсии, здесь специализация нашего шаблона с размерностью 0, объявляющая тип обычного std::vector<T>.

template<typename T>
struct VectorGenerator<T, 0>
{
    typedef std::vector<T> type;
};

Как им пользоваться?

Теперь мы можем объявить вектор v типа VectorGenerator<T, N>::type :

VectorGenerator<double, 4>::type v; // v as a depth of 4 and handle double

Но это не очень читабельно или удобно, и довольно многословно. Давайте введем новые имена для наших типов.

Это идеальный случай для псевдонимов шаблонов с (C++11 ) using ключевое слово для псевдонима. У нас есть 2 разных способа псевдонимов:

1) Объявите псевдоним для определенного измерения и типа, здесь мы называем его V3 для N=3 и T=double :

using V3 = VectorGenerator<double, 3>::type;  // Alias

V3 v;                                         // Use the alias

Or,

2) Объявите псевдоним шаблона для определенного типа, оставив измерение в качестве параметра шаблона:

template <size_t N> 
using V = typename VectorGenerator<double, N>::type;  // Alias

V<3> v;                                              // Use the Alias

Окончательный пример кода:

template<typename T, size_t N>
struct VectorGenerator
{
    typedef std::vector< typename VectorGenerator<T, N-1>::type > type;
};

template<typename T>
struct VectorGenerator<T, 0>
{
    typedef std::vector<T> type;
};

// Alias for V3, V2 ... usage
using V3 = VectorGenerator<double, 3>::type;
using V2 = VectorGenerator<double, 2>::type;

// Alias for V <k> usage
template <size_t N> 
using V = typename VectorGenerator<double, N>::type;

int main() {

    V<3> v3;
    V<2> v2;
    v3.push_back(v2);
    return 0;
}

Примечания:

  • Рассмотрим библиотеку Boost MultiDimensional Array.
  • Я не уверен, какова ваша конечная цель, но это вполне может быть излишним.
  • Что касается вашего второго редактирования, теперь легко объявить tuple с несколькими векторами разных размеров:

Пример:

auto tower = std::tuple<V<1>, V<2>, V<3>>(v1, v2, v3);

Для создания общего кортежа из нескольких «башен» @mpark предоставил рабочее решение C++14. a>, я адаптирую его здесь к моему примеру кода:

template <typename T>
struct identity { using type = T; };

// Generate a tuple of towers by mapping index_sequence over gen_tower.
template <typename T, std::size_t... Is>
std::tuple<VectorGenerator<T, Is>...> gen_towers_impl(std::integer_sequence<Is...>);

// Make an index_sequence for N and use gen_towers_impl.
template <typename T, std::size_t N>
struct gen_towers
    : identity<decltype(gen_towers_impl<T>(std::make_index_sequence<N>()))> {};

// Convenience type aliases
template <typename T, std::size_t N>
using gen_towers_t = typename gen_towers<T, N>::type;

Вам понадобится -std=c++1y для его компиляции (и включите заголовки <utility> и <tuple>)

См. рабочий пример здесь.

person quantdev    schedule 28.06.2014
comment
Большое спасибо за ответ и указатели для увеличения многомерного массива. Есть ли способ записать результаты как что-то вроде v‹3›, v‹2›?? Пожалуйста, смотрите мое обновление. - person tinlyx; 28.06.2014
comment
Что ты конкретно имеешь ввиду ? мне это непонятно. То, что вы имели в виду под V‹3›, это, наверное, мой V3. Нет ? - person quantdev; 28.06.2014
comment
@TingL template <size_t N> using V = VectorGenerator<double, N>; V<3> v; - person nwp; 28.06.2014
comment
Это серьезно умно. Я знаю, как это сделать с помощью макросов, но никогда раньше не видел, чтобы это делалось в шаблонах. - person david.pfx; 28.06.2014
comment
Я бы следовал обычному соглашению и изменил ::value на ::type, в противном случае хороший ответ. - person Daniel Frey; 28.06.2014
comment
@quantdev, я имею в виду, что мне нужно не только сгенерировать v1, v2 и v3, но и сгенерировать v_N для параметра N. Вероятно, это лучше всего описывается кортежем. Пожалуйста, смотрите мой РЕДАКТИРОВАТЬ. - person tinlyx; 28.06.2014
comment
Ну, вы говорите, что N известно во время компиляции, поэтому просто выполните auto tower = std::tuple<V<1>, V<2>, V<3>>(v1, v2, v3);, что в этом не так? - person quantdev; 28.06.2014
comment
Лично я бы определил VectorGenerator<T, 1> как одномерный вектор вместо VectorGenerator<T, 0>. - person zneak; 29.06.2014

Вы можете найти аналогичный вопрос, но касающийся std::map в сокращенном синтаксисе для карта c++ в карте.

Вот один для std::vector.

#include <iostream>
#include <vector>

template<int N, typename V>
struct NVector { typedef std::vector<typename NVector<N-1, V>::type> type; };

template<typename V>
struct NVector<1, V> { typedef std::vector<V> type; };

int main(int argc, const char *argv[]) {

   NVector<1, int>::type v1(10, 0);

   NVector<2, int>::type v2;
   v2.push_back(v1);

   NVector<3, int>::type v3;
   v3.push_back(v2);

   for ( int i = 0; i < 10; ++i )
   {
      std::cout << v3[0][0][i] << " ";
   }
   std::cout << std::endl;
}

Выход:

0 0 0 0 0 0 0 0 0 0 

Обновить

Вы можете упростить использование этих векторов с помощью объявления using.

#include <iostream>
#include <vector>

template<int N, typename V>
struct NVector { typedef std::vector<typename NVector<N-1, V>::type> type; };

template<typename V>
struct NVector<1, V> { typedef std::vector<V> type; };

template<int N, typename Val>
using V = typename NVector<N, Val>::type;

int main(int argc, const char *argv[]) {

   V<1, int> v1(10, 0);

   V<2, int> v2;
   v2.push_back(v1);

   V<3, int> v3;
   v3.push_back(v2);

   for ( int i = 0; i < 10; ++i )
   {
      std::cout << v3[0][0][i] << " ";
   }
   std::cout << std::endl;
}

Если вы хотите упростить его, предполагая, что тип значения равен int, вы можете использовать:

#include <iostream>
#include <vector>

template<int N, typename V>
struct NVector { typedef std::vector<typename NVector<N-1, V>::type> type; };

template<typename V>
struct NVector<1, V> { typedef std::vector<V> type; };

template<int N>
using V = typename NVector<N, int>::type;

int main(int argc, const char *argv[]) {

   V<1> v1(10, 0);

   V<2> v2;
   v2.push_back(v1);

   V<3> v3;
   v3.push_back(v2);

   for ( int i = 0; i < 10; ++i )
   {
      std::cout << v3[0][0][i] << " ";
   }
   std::cout << std::endl;
}
person R Sahu    schedule 28.06.2014
comment
Спасибо. Есть ли способ определить результаты как что-то вроде v‹3›, v‹2›?? Пожалуйста, смотрите мое обновление. - person tinlyx; 28.06.2014
comment
@R Саху. Я думаю, что я действительно ищу, это создать кортеж (v1, v2,..., v_N) для параметра N или пакета параметров (на основе того, что вы показали). Пожалуйста, смотрите мой РЕДАКТИРОВАТЬ. Спасибо - person tinlyx; 28.06.2014
comment
@TingL, не глядя на вариант использования, трудно предложить то, что вы ищете. Я надеюсь, что у вас достаточно ответов, чтобы работать с ним. - person R Sahu; 28.06.2014

Я не буду вдаваться в подробности создания одной башни, так как это объясняется другими ответами здесь. Вот моя версия gen_tower<T, I>, которая генерирует башню векторов глубиной I.

Например, gen_tower_t<int, 2> равно std::vector<std::vector<T>>.

// Useful for defining meta-functions compactly.
template <typename T>
struct identity { using type = T; };

gen_tower‹T, I›

// Forward declaration.
template <typename T, std::size_t I>
struct gen_tower;

// Convenience type alias.
template <typename T, std::size_t I>
using gen_tower_t = typename gen_tower<T, I>::type;

// Base case.
template <typename T>
struct gen_tower<T, 0> : identity<T> {};

// Wrap std::vector around tower of depth I - 1.
template <typename T, std::size_t I>
struct gen_tower : identity<std::vector<gen_tower_t<T, I - 1>>> {};

gen_towers‹T, N›

Теперь мы можем использовать std::index_sequence для определения N башен.

// Generate a tuple of towers by mapping index_sequence over gen_tower.
template <typename T, std::size_t... Is>
std::tuple<gen_tower_t<T, Is>...> gen_towers_impl(std::index_sequence<Is...>);

// Make an index_sequence for N and use gen_towers_impl.
template <typename T, std::size_t N>
struct gen_towers
    : identity<
          decltype(gen_towers_impl<T>(std::make_index_sequence<N>()))> {};

// Convenience type aliases
template <typename T, std::size_t N>
using gen_towers_t = typename gen_towers<T, N>::type;

Примеры

static_assert(std::is_same<gen_tower_t<int, 0>, int>::value, "");

static_assert(std::is_same<
                  gen_tower_t<int, 2>,
                  std::vector<std::vector<int>>>::value, "");

static_assert(std::is_same<
                  gen_towers_t<int, 2>,
                  std::tuple<int, std::vector<int>>>::value, "");

static_assert(std::is_same<
                  gen_towers_t<int, 3>,
                  std::tuple<int,
                             std::vector<int>,
                             std::vector<std::vector<int>>>>::value, "");

int main() {}
person mpark    schedule 28.06.2014