c++11 променливо програмиране, как да дефинираме кула от вектори

Как (ако е възможно) мога да използвам c++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;
}

Също така, това ще бъде ли по-лесно в c++14?

Благодаря,

--РЕДАКТИРАНЕ--

Само за пояснение, това, което имам предвид под „кула от вектори“, се описва най-добре от N-кортеж (v_1, v_2, ..., v_N), където N е интегрален параметър на шаблона. v_1 е от вектор, v_2 е от вектор> и т.н.

--РЕДАКТИРАНЕ2--

Досега отговорите на 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. Мога да чета 2-измерни масиви и 3-измерни. Но би било хубаво да можете да четете масиви с всякакви измерения.


person tinlyx    schedule 28.06.2014    source източник
comment
можете ли да дадете пример как възнамерявате да използвате тези N масиви или е упражнение да ги дефинирате?   -  person Cheers and hth. - Alf    schedule 28.06.2014
comment
@Alf, това е чисто от лични интереси. моля, вижте актуализацията в P.S. наздраве.   -  person tinlyx    schedule 28.06.2014


Отговори (3)


Няма нужда от variadics, рекурсивен 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.
  • Не съм сигурен каква е крайната ви цел, но това може да е прекалено.
  • Що се отнася до втората ви редакция, декларирането на tuple с множество вектори с различни измерения вече е лесно:

Пример:

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

За генерирането на генерични кортежи от множество „кули“ @mpark даде работещо C++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> заглавки)

Вижте работещ пример тук.

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