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_listBoost 有一个multi-array 库,它使用自定义对象来构造它们的多维数组。这是一个非常好的方法;我建议你研究(或者更好的是,使用)他们的代码。
【讨论】:
有趣的东西。我当然可以使用他们的课程,但实施这种事情总是有教育意义的。我有一些使用 va_list ( ... ) 的东西,但它似乎......脆弱?不太确定这个词看起来不是很健壮。 任何使用 C 可变参数的解决方案都不是类型安全的,因为无法限制传递给函数的参数类型或数量,也无法在运行时验证其正确性。以上是关于n维数组构造函数的模板规范的主要内容,如果未能解决你的问题,请参考以下文章