С++ 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-мерные. Но было бы неплохо читать массивы любых измерений.
Ответы
Ответ 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() {}