【问题标题】:c++11 variadic programming, how to define a tower of vectorsc++11 可变参数编程,如何定义向量塔
【发布时间】:2014-08-19 05:52:03
【问题描述】:

如何(如果可能)使用 c++11 可变参数编程在函数体中定义一系列 vector,(或者换句话说,一系列 N 维数组,@ 递减987654323@'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 定义 N 元组的问题,例如 3,但无法为未指定的 N 生成元组。除了答案中的功能之外,我还需要一个可以像gen_tower&lt;N&gt; 一样使用的函数,它返回tuple(v1,v2,...,vN)

考虑使用可变参数编程计算阶乘的示例。除了手动写出任何特定表达式&lt;1*2*3&gt; 的能力之外,我还需要一个函数来计算任何N 的阶乘factorial&lt;N&gt;()。 (这就是为什么我询问可变参数编程以及c++14 是否会使其更容易的原因。)

附言

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

【问题讨论】:

  • 您能否举例说明您打算如何使用这些 N 个数组,或者定义它们是一个练习?
  • @Alf,这纯粹是出于个人利益。请参阅 P.S. 中的更新。干杯。

标签: c++ templates c++11 metaprogramming c++14


【解决方案1】:

不需要可变参数,递归 typedef 足以在编译时生成这些类型。


是如何实现的?

1) 提供带有 2 个参数的模板:向量元素类型 (T) 和所需的结构维度 (size_t N)。声明一个 typedef 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&lt;T&gt; 的类型。

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

如何使用?

我们现在可以声明VectorGenerator&lt;T, N&gt;::type 类型的向量v

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

但它的可读性和方便性都不是很高,而且非常冗长。让我们为我们的类型引入新名称。

这是 template aliasing 的完美案例,使用 (C++11) using 关键字进行别名。我们有两种不同的别名方式:

1) 为特定维度和类型声明一个别名,这里我们将其称为 V3 for N=3T=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 给出了a working C++14 solution,我在这里将其改编为我的代码示例:

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 来编译它(并包括&lt;utility&gt;&lt;tuple&gt; 标头)

查看工作示例here

【讨论】:

  • 非常感谢boost多维数组的回答和指点。有没有办法把结果写成 v, v??请查看我的更新。
  • 你到底是什么意思?我不清楚。我猜你所说的“V”是我的V3。没有?
  • @TingL template &lt;size_t N&gt; using V = VectorGenerator&lt;double, N&gt;; V&lt;3&gt; v;
  • 这真是太聪明了。我知道如何使用宏来做到这一点,但我以前从未在模板中看到过。
  • 我会按照通常的约定将::value更改为::type,否则很好回答。
【解决方案2】:

您可以在shorthand syntax for c++ map in map 找到类似的问题,但与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;
}

【讨论】:

  • 谢谢。有没有办法将结果定义为 v, v??请查看我的更新。
  • @R Sahu。我认为我真正要寻找的是为参数 N 或参数包(根据您显示的内容)生成 (v1, v2, ..., v_N) 的元组。请参阅我的编辑。谢谢
  • @TingL,如果不查看用例,很难建议您要查找的内容。我希望你有足够的答案来运行它。
【解决方案3】:

我不会详细介绍生成单塔,因为此处的其他答案已对此进行了解释。这是我的gen_tower&lt;T, I&gt; 版本,它会生成一个向量深度塔I

例如,gen_tower_t&lt;int, 2&gt;std::vector&lt;std::vector&lt;T&gt;&gt;

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

gen_tower

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

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

【讨论】:

    猜你喜欢
    • 2014-01-09
    • 2010-12-15
    • 1970-01-01
    • 2012-08-14
    • 2013-08-03
    • 1970-01-01
    • 2012-03-03
    相关资源
    最近更新 更多