Подтвердить что ты не робот

С++ 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?

Спасибо,

- EDIT -

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

- EDIT2 -

До сих пор ответы квантведа и 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. Я могу читать 2-мерные массивы и 3-мерные. Но было бы неплохо читать массивы любых измерений.

4b9b3361

Ответ 1

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


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

1) Предоставить шаблон с двумя аргументами: тип векторных элементов (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

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

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

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

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

V3 v;                                         // Use the alias

Или

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 library.
  • Я не уверен, какова ваша конечная цель, но это может быть излишним.
  • Что касается вашего второго редактирования, объявление tuple с несколькими векторами разных размеров теперь легко:

Пример:

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

Для генерации типичных кортежей нескольких "башен", @mpark дал рабочее С++ 14 решение, я его адаптирую здесь к моему образцу кода:

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>)

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

Ответ 2

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

Здесь один для 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;
}

Ответ 3

Я не буду вдаваться в подробности о создании одной башни, поскольку она объясняется другими ответами здесь. Вот моя версия 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, я >

// 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() {}