c++11可变编程,如何定义一个向量塔

c++11 variadic programming, how to define a tower of vectors

本文关键字:一个 向量塔 定义 编程 何定义 c++11      更新时间:2023-10-16

如何(如果可能的话)使用c++11可变编程来定义函数体中的一系列vector 's,(或者换句话说,一系列N维数组,N 's递减直到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属于向量>,以此类推。

——EDIT2

到目前为止,quantdev和R的答案已经成功地解决了为任意固定N(如3)定义N元组的问题,但无法为未指定的N生成元组。除了答案中的功能外,我还需要一个函数,可以像gen_tower<N>一样使用,返回tuple(v1,v2,...,vN)

考虑使用可变编程来计算阶乘的例子。我需要一个函数来计算任何N的阶乘factorial<N>(),以及手动写出任何特定表达式<1*2*3>的能力。(这就是为什么我问可变编程和c++14是否会使它更容易的原因。)

公立小学

纯粹出于个人兴趣,我希望这个序列能够实现一个可以从文件中读取n维数组的泛型函数。我不知道具体是怎么做的,但我认为在第一步,我应该能够定义最终的N维数组,以及从N-11k的中间k维数组。我可以读取二维数组和三维数组。但是如果能够读取任意维度的数组就更好了

不需要变量,一个递归的typedef足以在编译时生成这些类型。


如何实现

1)提供带有2个参数的模板:vector元素类型(T)和所需的结构维数(size_t N)。声明一个类型定义type:它将基于深度为N-1的模板实例化的type的声明,因此递归。

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;
};

如何使用

现在可以声明VectorGenerator<T, N>::type类型的向量v:

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

但是它不是很好读,也不方便,而且相当冗长。让我们为我们的类型引入新的名称。

这是模板混叠的完美案例,使用(c++ 11) using关键字进行混叠。我们有两种不同的混叠方式:

1)为特定的维度和类型声明别名,这里我们称其为N=3T=double的V3:

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多维数组库。
  • 我不确定你的最终目标是什么,但这可能是一个过度杀伤。
  • 对于您的第二次编辑,声明具有不同维度的多个矢量的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>头文件)

你可以找到一个类似的问题,但处理std::map在c++ 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之前

可以通过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;
}

我不会在生成单个塔的过程中讨论太多细节,因为它已经在这里的其他答案中进行了解释。这是我的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,>

现在我们可以使用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() {}