基于模板函数参数的指针级别 return 类型

Pointer level return type based on template function arguments

对于张量 class 我想要一个创建函数的模板,例如

double* mat(int size1);
double** mat(int size1, int size2);
double*** mat(int size1, int size2, int size3);

即return 类型的指针级别取决于输入的数量。 基本情况就是

double* mat(int size1){
  return new double[size1];
}

我认为可变参数模板可以以某种方式解决问题

template<typename T, typename... size_args>
T* mat(int size1, size_args... sizes) {
  T* m = new T[size1];
  for(int j = 0; j < size1; j++){
    m[j] = mat(sizes...);
  }
  return m;
}

推导 m[j] = mat(sizes...); 中的模板参数似乎不起作用,尽管需要多次递归调用,如

auto p = mat<double**>(2,3,4);

但是我如何在递归调用中提供参数?正确的参数应该是 T 以下一个指针级别,即 T=double***mat<double**>。 我觉得我在这里错过了一些关于模板的重要内容。

您不能将 m 和 return 类型声明为 T*,因为它不在多维度中。

template<typename T, typename size_type>
auto mat(size_type size){return new T[size];}

template<typename T, typename size_type, typename... size_types>
auto mat(size_type size, size_types... sizes){
    using inner_type = decltype(mat<T>(sizes...));
    inner_type* m = new inner_type[size];
    for(int j = 0; j < size; j++){
        m[j] = mat<T>(sizes...);
    }
    return m;
}

由于找不到更好的名称,我将调用模板 meta-function,它创建一个具有所需“深度”和类型的指针类型,即“递归指针”。这样的事情可以这样实现:

template <size_t N, class T>
struct RecursivePtr
{
    using type = RecursivePtr<N-1, T>::type*;
};

template <class T>
struct RecursivePtr<0, T>
{
    using type = T;
};

template <size_t N, class T>
using recursive_ptr_t = RecursivePtr<N, T>::type; 
例如

recursive_ptr_t<4, int> 创建 int****。因此,在您的情况下,您可以继续将 mat 函数实现为:

template <class... Args>
auto mat(Args... args)
{
    recursive_ptr_t<sizeof...(Args), double> m;
    
    // Runtime allocate your Npointer here.
    
    return m;
}

Demo

为 mat 函数增加类型安全性的一些想法是:

  1. 添加静态断言所有提供的类型都是大小
  2. 静态断言至少提供了一种尺寸

注意,当我在上面说运行时分配你的 Npointer 时,我的意思是:

template <class T, class... Sz>
void alloc_ar(T* &ar, size_t s1, Sz... ss)
{
    if constexpr (sizeof...(Sz))
    {
        ar = new T[s1];
        for (size_t i(0); i < s1; i++)
            alloc_ar(ar[i], ss...);
    }
    else
    {
        ar = new T[s1];
    }
}

制作了一个 Demo,其中我显示了分配,但没有显示释放。

一个合理的替代方案是分配一个连续的内存块(大小 == 维度的倍数)并在访问它时使用指向该块开头的多维指针作为语法糖。这也提供了一种更简单的方法来释放内存。

第二种选择是使用嵌套的向量向量(向量的向量...),其生成机制与 Npointer 相同。这消除了手动内存管理的需要,并且可能会迫使您将整个事情包装在更具表现力的 class:

template <class... Dims>
class mat
{
  template <size_t N, class T>
  struct RecursivePtr
  {
    using type = std::vector<RecursivePtr<N-1, T>::type>;
  };

  template <class T>
  struct RecursivePtr<0, T>
  {
    using type = T;
  };

  // This is the replacement to double***
  // translates to vector<vector<vector<double>>>
  RecursivePtr<N, T>::type _data; 

public:
  // construction, argument deduction etc ... 
};