n维数组构造函数的模板规范

Posted

技术标签:

【中文标题】n维数组构造函数的模板规范【英文标题】:Template Specifications for constructor of n-dimensional array 【发布时间】:2009-07-14 02:51:21 【问题描述】:

我正在实现一个 n 维数组类,它是一个模板,如下所示(注意数据存储在一个线性数组中,其长度是所有维度的乘积):

template< class valType, int rank >
class NDimensionalArray

public:

private:
    valType* m_data;
    int* m_dimensions;
    int m_rank;
;

所以这个想法是用户(我)可以指定一个等级为 2 且具有特定维度的数组,即:

NDimensionalArray<double,2> matrix(10,10);

现在的困难在于为 1->n 维专门构造构造函数,每个构造函数接受 n 个参数,其中 n 是数组的秩。现在我想到了在 printf() 中使用类似 valarray 的方法,但是这样定义了一个 2 维的一维数组,即:

NDimensionalArray<double,1> matrix(10,10);

将是完全可以接受的行为。我可以使用一些巧妙的技巧让编译器进行重复吗?实际上,只要我知道等级,并且具有每个维度的长度,构造函数就可以是通用的:


    int nElements = m_dimensions[0];
    for ( int i=1 ; i<m_rank ; ++i )
        nElements *= m_dimensions[i];
    m_data = new valType[nElements];

编辑:请注意,访问者也需要类似的操作。

我还考虑过构造函数的选项,如下所示:

NDimensionalArray( const NDimensionalArray<int,1>& dimensions );

可以这样使用:

NDimensionalArray<int,1> dimVec(2); // Need a specification for 1-dimensional arrays.
dimVec(0) = 10;
dimVec(1) = 10;
NDimensionalArray<double,2> matrix(dimVec);

这将是一个可行的解决方案,但与我想要的用途相比,它很难看。访问多维数组也会变得非常痛苦,并且必须为每次访问构建一个维度向量非常缓慢。

【问题讨论】:

出于好奇,难道boost::multi_array 完全不需要你编写这样一个类就可以做你想做的事吗? 确实会。这更像是一个“我想知道我怎么能做到”的项目。你知道的好奇心。 【参考方案1】:

好的,我已经玩了一段时间了。这是一些模板元编程黑客,可以做一些接近你想要的事情。它允许您内联指定所有维度,它不执行任何动态内存分配或其他类似的事情。此外,使用良好的 C++ 编译器(我使用 VC++ /O2 选项进行了测试),代码将被完全内联,没有复制(事实上,对我来说,它在调用点内联了整个 NDimensionalArray 构造函数)。它将在编译时完全进行类型检查,并且不会让您传递太少或太多的维度。并且它可以重复用于索引器。如下:

template<class T, int N>
class value_pack : private value_pack<T, N-1>

public:

    enum  size = N ;

    value_pack(value_pack<T, N-1> head, const T& tail)
        : value_pack<T, N-1>(head)
        , value(tail)
    
    

    value_pack<T, N+1> operator() (const T& tail) const
    
        return value_pack<T, N+1>(*this, tail);
    

    template<int I>
    const T& get() const
    
        return this->value_pack<T, I+1>::value;
    

protected:

    const T value;
;

template<class T>
struct value_pack<T, 0>

;

struct

    template <class T>
    value_pack<T, 1> operator() (const T& tail) const
    
        return value_pack<T, 1>(value_pack<T, 0>(), tail);
    
 const values;


template <class ValType, int Rank>
struct NDimensionalArray

    NDimensionalArray(value_pack<ValType, Rank> values)
    
        // ...
    
;


int main()

    NDimensionalArray<int, 3> a(values(1)(2)(3));

【讨论】:

+1 非常棘手,先生。递归模板编程,绝对可以工作,并且可能是最快的解决方案。【参考方案2】:

我认为最好的解决方案是采用整数向量并让构造函数根据模板参数“rank”对其进行验证。

NDimensionalArray matrix(std::vector<int> matrixDimensions) 

    if (matrixDimensions.size() != rank) 
    
        throw SomeException();
    

    ...

我认为这里没有任何编译器技巧可以替代。 (除了 perhps 使用宏,如果你能想到一些东西,虽然严格来说这不是 编译器 把戏。)

【讨论】:

是的,我很担心这一点,请参阅上面的编辑。我真的希望找到一个很好的干净解决方案,可以让课程“自然”使用。当我们考虑访问器时,这确实成为一个问题。我的意思是构造函数中的向量是一回事,但是如果我们每次都必须使用一个向量,那么访问一个包含 100 万个元素的数组将会非常慢。 @DeusAduro 如果您事先指定大小并访问对处理器缓存友好的元素,这并不慢。我看不出有什么办法让它与众不同,因为你需要在堆上分配一个数组,而不是在堆栈上 你是对的。在这种情况下,请使用可变参数 thing。在构造函数和访问器/修改器中都进行验证。这里的问题是您不能“模板化”函数的参数数量。 @Edison 我认为它会很慢,并且在这种情况下它与缓存友好无关,事实上我必须为每次访问构建一个向量或其他一些索引器,这些对象本质上是临时的,我进行访问然后丢弃它。现在我可以实现更复杂的方案,其中索引器可以沿着数组的特定维度进行迭代......我正在寻找一个效率适中的简单接口。【参考方案3】:

不是直接的答案,但请查看 blitz 库。

【讨论】:

【参考方案4】:

按照目前的标准化,在 C++ 中没有很好的方法。在 C++0x 中,您将能够使用模板参数包来近似(我认为我的语法正确,但不确定 requires 中的扩展):

template <class ValType, int Rank>
struct NDimensionalArray

    template <class... Args>
    requires std::SameType<Args, ValType>... && std::True<sizeof...(Args) == Rank>
    NDimensionalArray(Args... values)
    
        ...
    
;

【讨论】:

听起来是个很酷的功能,语法有点超出我的理解,没有一个更简单的例子啊,但是很酷。【参考方案5】:

您可以使用 std::tr1::array。嗯:

#include <array>

template< class valType, int rank >
class NDimensionalArray

public:
   NDimensionalArray(const std::tr1::array<rank>& dims);
   // ...
;

NDimensionalArray<double,2> foo(10,10);

NDimensionalArray<double,2> bar(10); // second dimension would be 0

NDimensionalArray<double,1> baz(10,10); // compile error?

我实际上不确定这是否有效!我会通过 Comeau 运行它。

已编辑根据 cmets,这种方法看起来更像:

std::tr1::array<2> dims = 10, 10;
NDimensionalArray<double,2> foo(dims);

【讨论】:

这行不通,因为聚合初始化器只在初始化变量时才被允许,而不是在任意表达式中(例如函数和构造函数参数)。 无法让 Comeau 找到 tr1 数组标题,但现在你这么说,我想我记得它过去咬过我。 不要引用我的话,但我相信这将是有效的 C++0x 代码,因为初始化列表变为完整的类型(在这种情况下为 std::initializer_list): en.wikipedia.org/wiki/C%2B%2B0x#Initializer_lists 还有:NDimensionalArrayfoo( (std::tr1::array)10,10 );可以做到这一点(它与 g++ >= 4.0)【参考方案6】:

Boost 有一个multi-array 库,它使用自定义对象来构造它们的多维数组。这是一个非常好的方法;我建议你研究(或者更好的是,使用)他们的代码。

【讨论】:

有趣的东西。我当然可以使用他们的课程,但实施这种事情总是有教育意义的。我有一些使用 va_list ( ... ) 的东西,但它似乎......脆弱?不太确定这个词看起来不是很健壮。 任何使用 C 可变参数的解决方案都不是类型安全的,因为无法限制传递给函数的参数类型或数量,也无法在运行时验证其正确性。

以上是关于n维数组构造函数的模板规范的主要内容,如果未能解决你的问题,请参考以下文章

我需要 C++ 数组类模板,它是固定大小、基于堆栈且不需要默认构造函数

多维数组和矩阵

子类构造函数

常见的构造函数类型

模板类构造函数中的动态分配

编写可变参数模板构造函数