无论存储顺序如何,元素多维数组迭代

时间:2015-02-04 02:33:34

标签: c++ c++11 multidimensional-array iterator

我有一个n维数组类,我希望能够按特定顺序迭代元素,先说行,因此无论存储顺序如何(行或列主要)。

数组类的签名是template<typename T, StorageOrder T_storageOrder, std::size_t ... T_dimensions> class Array,我有一个storageIndex()函数,它将索引列表转换为1-D索引以检索内部数据,并且有{{1}迭代逻辑的类。

一切都运行良好,但对于列主要情况下的结束迭代器。结束迭代器背后的想法是使用ElementWiseIterator函数从超出storageIndex()模板参数定义的数组形状范围的索引列表中检索1-D索引。换句话说,如果我有一个T_dimensions数组,那么我正在尝试检索位于2 × 3的索引,但这(正确地)在使用列专业时返回第3个元素order数组,而不是返回第7个(不存在的)元素,因为我需要表示结束迭代器。

这让我相信我的方法是有缺陷的,但我现在没有其他想法,我正在寻找一些灵感来提出一个干净和通用的方法。我查看了{2, 0},但无法理解实施的任何内容。

我还想知道numpy.nditer是否有点不能简化,因为它目前需要保存一些数据。

请在下面找到代码的缩减。

ElementWiseIterator

代码输出:

#include <array>
#include <cstddef>
#include <iostream>
#include <iterator>

template<typename T>
constexpr T product(T unique)
{ return unique; }

template<typename T, typename ... T_Others>
constexpr T product(T first, T second, T_Others ... others)
{ return product(first * second, others ...); }

enum class StorageOrder {
    rowMajor,
    columnMajor
};

template<StorageOrder> struct StorageOrderTag {};
using RowMajorStorageOrderTag = StorageOrderTag<StorageOrder::rowMajor>;
using ColumnMajorStorageOrderTag = StorageOrderTag<StorageOrder::columnMajor>;


// - Converts a list of indices for a specific shape array into a 1-D index.

template<typename T_Shape>
std::size_t storageIndex(const T_Shape &indices, const T_Shape &shape, RowMajorStorageOrderTag)
{
    std::size_t i = 0;
    std::size_t out = indices[i];
    while (i++ < indices.size() - 1) {
        out = indices[i] + shape[i] * out;
    }

    return out;
}

template<typename T_Shape>
std::size_t storageIndex(const T_Shape &indices, const T_Shape &shape, ColumnMajorStorageOrderTag)
{
    std::size_t i = indices.size() - 1;
    std::size_t out = indices[i];
    while (i-- > 0) {
        out = indices[i] + shape[i] * out;
    }

    return out;
}


//- Element-wise iterator.

template<
    typename T,
    typename T_Data,
    StorageOrder T_storageOrder,
    std::size_t T_dimensionality
>
class ElementWiseIterator
    : public std::iterator<std::bidirectional_iterator_tag, T>
{
private:
    using Shape = std::array<std::size_t, T_dimensionality>;

public:
    T & operator*() const
    { return *_currentElement; }

    ElementWiseIterator & operator++()
    {
        std::size_t i = _shape.size();
        while (i-- > 0) {
            if (_currentIndices[i] < _shape[i] - 1 || i == 0) {
                ++_currentIndices[i];
                break;
            }
        }

        for (++i; i < _currentIndices.size(); ++i) {
            _currentIndices[i] = 0;
        }

        setCurrentElement();
        return *this;
    }

    friend bool operator==(const ElementWiseIterator &iterator1, const ElementWiseIterator &iterator2)
    { return iterator1._currentElement == iterator2._currentElement; }

    friend bool operator!=(const ElementWiseIterator &iterator1, const ElementWiseIterator &iterator2)
    { return !(iterator1 == iterator2); }

private:
    ElementWiseIterator(T_Data *data, const Shape &indices, const Shape &shape)
        : _currentElement(nullptr),
          _data(data),
          _currentIndices(indices),
          _shape(shape)
    {
        setCurrentElement();
    }

    void setCurrentElement()
    {
        std::size_t index = storageIndex(
            _currentIndices,
            _shape,
            StorageOrderTag<T_storageOrder>()
        );

        _currentElement = &(*_data)[index];
    }

    T *_currentElement;
    T_Data *_data;
    Shape _currentIndices;
    Shape _shape;

    template<typename, StorageOrder, std::size_t ...> friend class Array;
};


//- Array class.

template<typename T, StorageOrder T_storageOrder, std::size_t ... T_dimensions>
class Array
{
public:
    static constexpr std::size_t size()
    { return product(T_dimensions ...); }

    using Shape = std::array<std::size_t, sizeof ... (T_dimensions)>;

    static constexpr Shape shape()
    { return {T_dimensions ...}; }

protected:
    using Storage = std::array<T, size()>;

public:
    using Iterator = typename Storage::iterator;
    using ElementWiseIterator = ElementWiseIterator<
        T,
        Storage,
        T_storageOrder,
        sizeof ... (T_dimensions)
    >;

    Iterator begin()
    { return _data.begin(); }

    Iterator end()
    { return _data.end(); }

    ElementWiseIterator elementWiseBegin()
    { return ElementWiseIterator(&_data, {0}, shape()); }

    ElementWiseIterator elementWiseEnd()
    {
        // Set the current iterator indices to the first out of range element.
        // Ie: for an a 2x3 array, that would be {2, 0}.
        Shape shape = this->shape();
        return ElementWiseIterator(&_data, {shape[0]}, shape);
    }

    T & operator[](std::size_t index)
    { return _data[index]; }

    const T & operator[](std::size_t index) const
    { return _data[index]; }

private:
    Storage _data;
};


template<typename T, StorageOrder T_storageOrder, std::size_t ... T_dimensions>
void printDebug(Array<T, T_storageOrder, T_dimensions ...> &array)
{
    std::size_t i = 0;
    auto it = array.elementWiseBegin();
    for (; i < array.size(); ++i, ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
}


int main(int argc, char **argv)
{
    Array<int, StorageOrder::rowMajor, 2, 3> rowArray2d;
    Array<int, StorageOrder::columnMajor, 2, 3> colArray2d;

    Array<int, StorageOrder::rowMajor, 4, 2, 3> rowArray3d;
    Array<int, StorageOrder::columnMajor, 4, 2, 3> colArray3d;

    {
        std::cout << "\nTest case 1\n"
                  << "-----------\n"
                  << "Both arrays represent the same 2x3 matrix:\n"
                  << "  0 1 2\n"
                  << "  3 4 5"
                  << std::endl;

        rowArray2d[0] = 0; rowArray2d[1] = 1; rowArray2d[2] = 2;
        rowArray2d[3] = 3; rowArray2d[4] = 4; rowArray2d[5] = 5;

        colArray2d[0] = 0; colArray2d[2] = 1; colArray2d[4] = 2;
        colArray2d[1] = 3; colArray2d[3] = 4; colArray2d[5] = 5;

        // Below returns 0 1 2 3 4 5 as expected.
        std::cout << "element-wise iteration over rowArray2d:\n  ";
        for (auto it = rowArray2d.elementWiseBegin(); it != rowArray2d.elementWiseEnd(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)" << std::endl;

        // Below returns only 0 instead of 0 1 2 3 4 5.
        std::cout << "element-wise iteration over colArray2d:\n  ";
        for (auto it = colArray2d.elementWiseBegin(); it != colArray2d.elementWiseEnd(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)" << std::endl;

        // But if we increment using the `elementWiseBegin` iterator and use the
        // index number as a stop condition, then it works well.
        std::cout << "debug element-wise iteration over colArray2d:\n  ";
        printDebug(colArray2d);


        std::cout << "internal 1-D data iteration over rowArray2d:\n  ";
        for (auto it = rowArray2d.begin(); it != rowArray2d.end(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)" << std::endl;

        std::cout << "internal 1-D data iteration over colArray2d:\n  ";
        for (auto it = colArray2d.begin(); it != colArray2d.end(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 3 1 4 2 5 (expected)" << std::endl;
    }

    {
        std::cout << "\nTest case 2\n"
                  << "-----------\n"
                  << "Both arrays share the same internal 1-D representation:\n"
                  << "  0 1 2 3 4 5"
                  << std::endl;

        rowArray2d[0] = 0; rowArray2d[1] = 1; rowArray2d[2] = 2;
        rowArray2d[3] = 3; rowArray2d[4] = 4; rowArray2d[5] = 5;

        colArray2d[0] = 0; colArray2d[2] = 2; colArray2d[4] = 4;
        colArray2d[1] = 1; colArray2d[3] = 3; colArray2d[5] = 5;

        // Below returns 0 1 2 3 4 5 as expected.
        std::cout << "element-wise iteration over rowArray2d:\n  ";
        for (auto it = rowArray2d.elementWiseBegin(); it != rowArray2d.elementWiseEnd(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)" << std::endl;

        // Below returns only 0 instead of 0 2 4 1 3 5.
        std::cout << "element-wise iteration over colArray2d:\n  ";
        for (auto it = colArray2d.elementWiseBegin(); it != colArray2d.elementWiseEnd(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 2 4 1 3 5 (expected)" << std::endl;

        // But if we increment using the `elementWiseBegin` iterator and use the
        // index number as a stop condition, then it works well.
        std::cout << "debug element-wise iteration over colArray2d:\n  ";
        printDebug(colArray2d);


        std::cout << "internal 1-D data iteration over rowArray2d:\n  ";
        for (auto it = rowArray2d.begin(); it != rowArray2d.end(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)" << std::endl;

        std::cout << "internal 1-D data iteration over colArray2d:\n  ";
        for (auto it = colArray2d.begin(); it != colArray2d.end(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)" << std::endl;
    }

    {
        // This is because the end iterator, pointing to the indices (2, 0) is
        // (fairly enough) converted into the index 2 by the `storageIndex` function,
        // instead of an index beyond the last element.
        // std::cout << "\ncolumn-major storage index at (2, 0):\n  "
        //           << storageIndex({colArray2d.shape()[0]}, colArray2d.shape(), ColumnMajorStorageOrderTag())
        //           << std::endl;
    }

    {
        std::cout << "\nTest case 3\n"
                  << "-----------\n"
                  << "Both arrays represent the same 4x2x3 matrix:\n"
                  << " 0  1  2    6  7  8    12 13 14    18 19 20\n"
                  << " 3  4  5    9 10 11    15 16 17    21 22 23\n"
                  << std::endl;

        rowArray3d[ 0] =  0; rowArray3d[ 1] =  1; rowArray3d[ 2] =  2;
        rowArray3d[ 3] =  3; rowArray3d[ 4] =  4; rowArray3d[ 5] =  5;

        rowArray3d[ 6] =  6; rowArray3d[ 7] =  7; rowArray3d[ 8] =  8;
        rowArray3d[ 9] =  9; rowArray3d[10] = 10; rowArray3d[11] = 11;

        rowArray3d[12] = 12; rowArray3d[13] = 13; rowArray3d[14] = 14;
        rowArray3d[15] = 15; rowArray3d[16] = 16; rowArray3d[17] = 17;

        rowArray3d[18] = 18; rowArray3d[19] = 19; rowArray3d[20] = 20;
        rowArray3d[21] = 21; rowArray3d[22] = 22; rowArray3d[23] = 23;

        colArray3d[ 0] =  0; colArray3d[ 8] =  1; colArray3d[16] =  2;
        colArray3d[ 4] =  3; colArray3d[12] =  4; colArray3d[20] =  5;

        colArray3d[ 1] =  6; colArray3d[ 9] =  7; colArray3d[17] =  8;
        colArray3d[ 5] =  9; colArray3d[13] = 10; colArray3d[21] = 11;

        colArray3d[ 2] = 12; colArray3d[10] = 13; colArray3d[18] = 14;
        colArray3d[ 6] = 15; colArray3d[14] = 16; colArray3d[22] = 17;

        colArray3d[ 3] = 18; colArray3d[11] = 19; colArray3d[19] = 20;
        colArray3d[ 7] = 21; colArray3d[15] = 22; colArray3d[23] = 23;

        // Below returns 0 1 2 3 4 5 as expected.
        std::cout << "element-wise iteration over rowArray3d:\n  ";
        for (auto it = rowArray3d.elementWiseBegin(); it != rowArray3d.elementWiseEnd(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (expected)" << std::endl;

        // Below returns only 0 instead of 0 1 2 3 4 5.
        std::cout << "element-wise iteration over colArray3d:\n  ";
        for (auto it = colArray3d.elementWiseBegin(); it != colArray3d.elementWiseEnd(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (expected)" << std::endl;

        // But if we increment using the `elementWiseBegin` iterator and use the
        // index number as a stop condition, then it works well.
        std::cout << "debug element-wise iteration over colArray3d:\n  ";
        printDebug(colArray3d);


        std::cout << "internal 1-D data iteration over rowArray3d:\n  ";
        for (auto it = rowArray3d.begin(); it != rowArray3d.end(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (expected)" << std::endl;

        std::cout << "internal 1-D data iteration over colArray3d:\n  ";
        for (auto it = colArray3d.begin(); it != colArray3d.end(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n  0 6 12 18 3 9 15 21 1 7 13 19 4 10 16 22 2 8 14 20 5 11 17 23 (expected)" << std::endl;
    }

    return 0;
}

修改

此迭代器的目的是在具有不同存储顺序的数组之间执行元素操作,例如比较元素是否相等。

因此,迭代器应该以预定义的顺序遍历数组元素(此处为row-major)。也就是说,在使用Test case 1 ----------- Both arrays represent the same 2x3 matrix: 0 1 2 3 4 5 element-wise iteration over rowArray2d: 0 1 2 3 4 5 0 1 2 3 4 5 (expected) element-wise iteration over colArray2d: 0 0 1 2 3 4 5 (expected) debug element-wise iteration over colArray2d: 0 1 2 3 4 5 internal 1-D data iteration over rowArray2d: 0 1 2 3 4 5 0 1 2 3 4 5 (expected) internal 1-D data iteration over colArray2d: 0 3 1 4 2 5 0 3 1 4 2 5 (expected) Test case 2 ----------- Both arrays share the same internal 1-D representation: 0 1 2 3 4 5 element-wise iteration over rowArray2d: 0 1 2 3 4 5 0 1 2 3 4 5 (expected) element-wise iteration over colArray2d: 0 0 2 4 1 3 5 (expected) debug element-wise iteration over colArray2d: 0 2 4 1 3 5 internal 1-D data iteration over rowArray2d: 0 1 2 3 4 5 0 1 2 3 4 5 (expected) internal 1-D data iteration over colArray2d: 0 1 2 3 4 5 0 1 2 3 4 5 (expected) Test case 3 ----------- Both arrays represent the same 4x2x3 matrix: 0 1 2 6 7 8 12 13 14 18 19 20 3 4 5 9 10 11 15 16 17 21 22 23 element-wise iteration over rowArray3d: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (expected) element-wise iteration over colArray3d: 0 1 2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (expected) debug element-wise iteration over colArray3d: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 internal 1-D data iteration over rowArray3d: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (expected) internal 1-D data iteration over colArray3d: 0 6 12 18 3 9 15 21 1 7 13 19 4 10 16 22 2 8 14 20 5 11 17 23 0 6 12 18 3 9 15 21 1 7 13 19 4 10 16 22 2 8 14 20 5 11 17 23 (expected) 方法初始化2 x 3数组的迭代器时,它应该指向索引elementWiseBegin()处的元素。递增时,它应指向(0, 0),然后指向(0, 1),然后指向(0, 2),依此类推。

这确保了在迭代过程中,位于索引(1, 0)的第一个数组中的元素可以与位于同一索引(0, 2)的第二个数组的元素进行比较,从而无论他们的存储顺序如何。

修改2

似乎对行/列主要存储订单的定义存在一些混淆。当我谈论存储顺序时,我指的是内存中的布局,按the definition on Wikipedia而不是vector orientation

不同的存储顺序不应改变数组呈现给用户的方式。实际上,(0, 2)数组将始终如下所示,其中元素代表其索引。

2 x 3

但是,不同的存储顺序可以做的是对内存表示中的元素进行不同的对齐,即:

+----+----+----+
| 00 | 01 | 02 |
+----+----+----+
| 10 | 11 | 12 |
+----+----+----+

在我的代码片段中使用// Row-major storage order. 00 01 02 10 11 12 // Column-major storage order. 00 10 01 11 02 12 直接设置内部数据时,根据之前的说法,这就是我期望数组的样子:

0 1 2 3 4 5

元素迭代器的目标是始终以// Row-major storage order. +---+---+---+ | 0 | 1 | 2 | +---+---+---+ | 3 | 4 | 5 | +---+---+---+ // Column-major storage order. +---+---+---+ | 0 | 2 | 4 | +---+---+---+ | 1 | 3 | 5 | +---+---+---+ 的顺序返回元素,而不管存储顺序如何。这就是为什么在使用00 01 02 10 11 12直接设置内部数据时,我希望这个逐元素的迭代器在使用行主存储顺序时以0 1 2 3 4 5的顺序返回元素,{{1}在列主要存储顺序的情况下。

4 个答案:

答案 0 :(得分:1)

将值0, 1, 2, 3, 4, 5,存储到1-D数组中,当存储器访问为行主要时,该数组表示行主要2-D数组(2行,3列):

存储订单:

[
    0, 1, 2,
    3, 4, 5
]

i.e., [0, 1, 2, 3, 4, 5]

将值0, 1, 2, 3, 4, 5,存储到1-D数组中,当存储器访问为行主要时,该数组表示列主要2-D数组(2行,3列):

存储订单:

[
    0, 2, 4,
    1, 3, 5
]

i.e., [0, 2, 4, 1, 3, 5]

你说:

  

此迭代器的目的是在具有不同存储顺序的数组之间执行元素操作,例如比较元素是否相等。

我认为这意味着行主数组应该以不同于列主数组的方式存储其数据,并且应该有一个特殊的迭代器,以便每个数组可以以相同的逻辑顺序迭代(例如,比较它们是否相同)。假设我理解了你的问题,那么行/列主要数组将在内存中,如上所示。但是,使用特殊迭代器强制执行行主要排序会导致每个数组以相同的逻辑顺序访问其值(即[0, 1, 2, 3, 4, 5]

代码输出(我添加了注释):

// This shows the storage order is as describe above
storage order: { 0, 1, 2, 3, 4, 5 }
storage order: { 0, 2, 4, 1, 3, 5 }

// This shows that both can be iterated in the same 'logical order'
r major order: { 0, 1, 2, 3, 4, 5 }
r major order: { 0, 1, 2, 3, 4, 5 }

/*********************************
 * Works for arbitrary N-D array *
 *********************************/

// 3-D
storage order: { 0, 1, 2, 3, 4, 5, 6, 7 }
storage order: { 0, 4, 2, 6, 1, 5, 3, 7 }
r major order: { 0, 1, 2, 3, 4, 5, 6, 7 }
r major order: { 0, 1, 2, 3, 4, 5, 6, 7 }

// 4-D
storage order: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }
storage order: { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }
r major order: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }
r major order: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }

Test case 1
-----------
Both arrays represent the same 2x3 matrix:
  0 1 2
  3 4 5
row major iteration over rowArray:
  0 1 2 3 4 5
  0 1 2 3 4 5 (expected)
row major iteration over colArray:
  0 1 2 3 4 5
  0 1 2 3 4 5 (expected)
internal 1-D data iteration over rowArray:
  0 1 2 3 4 5
  0 1 2 3 4 5 (expected)
internal 1-D data iteration over colArray:
  0 3 1 4 2 5
  0 3 1 4 2 5 (expected)
col major iteration over rowArray:
  0 3 1 4 2 5
  0 3 1 4 2 5 (expected)
col major iteration over colArray:
  0 3 1 4 2 5
  0 3 1 4 2 5 (expected)

Test case 2
-----------
Both arrays share the same internal 1-D representation:
  0 1 2 3 4 5
row major iteration over rowArray:
  0 1 2 3 4 5
  0 1 2 3 4 5 (expected)
row major iteration over colArray:
  0 2 4 1 3 5
  0 2 4 1 3 5 (expected)
internal 1-D data iteration over rowArray:
  0 1 2 3 4 5
  0 1 2 3 4 5 (expected)
internal 1-D data iteration over colArray:
  0 1 2 3 4 5
  0 1 2 3 4 5 (expected)
col major iteration over rowArray:
  0 3 1 4 2 5
  0 3 1 4 2 5 (expected)
col major iteration over colArray:
  0 1 2 3 4 5
  0 1 2 3 4 5 (expected)

代码:

支持任意维度的行主要或列主要迭代。

#include <array>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

enum class StorageOrder
{
    rowMajor,
    columnMajor
};

template<typename T>
constexpr T product(T t1, T t2)
{
    return t1 * t2;
}

template<typename T, typename... Ts>
constexpr T product(T t, Ts... ts)
{
    return t * product(ts...);
}

template<StorageOrder T_iterOrder, std::size_t... T_dimensions>
class ArrayIndexer
{
public:
    using Indices = const std::array<std::size_t, sizeof... (T_dimensions)>;

    static std::size_t computeIndex(const Indices& indices)
    {
        const std::array<std::size_t, sizeof... (T_dimensions)> indexSizes = { T_dimensions... };

        std::size_t index = 0;
        for (std::size_t i = 0; i < sizeof... (T_dimensions); ++i)
        {
            std::size_t result = 1;
            for (std::size_t j = i + 1; j < sizeof... (T_dimensions); ++j)
            {
                result *= indexSizes[j];
            }

            result *= indices[i];
            index += result;
        }

        return index;
    }
};

template<std::size_t... T_dimensions>
class ArrayIndexer<StorageOrder::columnMajor, T_dimensions...>
{
public:
    using Indices = std::array<std::size_t, sizeof... (T_dimensions)>;

    static std::size_t computeIndex(const Indices& indices)
    {
        const std::array<std::size_t, sizeof... (T_dimensions)> indexSizes = { T_dimensions... };

        std::size_t index = indices[0];
        for (std::size_t i = 1; i < sizeof... (T_dimensions); ++i)
        {
            std::size_t result = 1;
            for (std::size_t j = 0; j < i; ++j)
            {
                result *= indexSizes[j];
            }

            result *= indices[i];
            index += result;
        }

        return index;
    }
};

template<typename T, StorageOrder T_storageOrder, StorageOrder T_iterOrder, std::size_t... T_dimensions>
class ArrayIterator
{
public:
    using Storage = std::array<T, product(T_dimensions...)>;
    using IndexSizes = std::array<std::size_t, sizeof... (T_dimensions)>;
    using Indices = std::array<std::size_t, sizeof... (T_dimensions)>;

    ArrayIterator(Storage& array, const Indices& indices) :
        mArray(array)
    {
        IndexSizes indexSizes = { T_dimensions... };
        if (indices == indexSizes)
        {
            mIndex = mArray.size();
        }
        else
        {
            mIndex = ArrayIndexer<T_iterOrder, T_dimensions...>::computeIndex(indices);
        }
    }

    constexpr bool operator==(const ArrayIterator& other) const
    {
        return &mArray == &other.mArray && mIndex == other.mIndex;
    }

    constexpr bool operator!=(const ArrayIterator& other) const
    {
        return !operator==(other);
    }

    T& operator*()
    {
        return mArray[mIndex];
    }

    ArrayIterator& operator++()
    {
        ++mIndex;
        return *this;
    }

private:
    std::size_t mIndex;
    Storage &mArray;
};

template<typename T, std::size_t... T_dimensions>
class ArrayIterator<T, StorageOrder::rowMajor, StorageOrder::columnMajor, T_dimensions...>
{
public:
    using Storage = std::array<T, product(T_dimensions...)>;
    using IndexSizes = std::array<std::size_t, sizeof... (T_dimensions)>;
    using Indices = std::array<std::size_t, sizeof... (T_dimensions)>;

    ArrayIterator(Storage& array, const Indices& indices) :
        mIndexSizes({ T_dimensions... }),
        mIndices(indices),
        mArray(array)
    {
        if (mIndices == mIndexSizes)
        {
            for (std::size_t i = 0; i < mIndices.size() - 1; ++i)
            {
                mIndices[i] = 0;
            }
        }
    }

    constexpr bool operator==(const ArrayIterator& other) const
    {
        return &mArray == &other.mArray && mIndices == other.mIndices;
    }

    constexpr bool operator!=(const ArrayIterator& other) const
    {
        return !operator==(other);
    }

    T& operator*()
    {
        return mArray[ArrayIndexer<StorageOrder::rowMajor, T_dimensions...>::computeIndex(mIndices)];
    }

    ArrayIterator& operator++()
    {
        advance(0);
        return *this;
    }

private:
    void advance(std::size_t incrementIndex)
    {
        if (++mIndices[incrementIndex] == mIndexSizes[incrementIndex])
        {
            if (incrementIndex < sizeof... (T_dimensions) - 1)
            {
                mIndices[incrementIndex] = 0;
                advance(incrementIndex + 1);
            }
        }
    }

private:
    IndexSizes mIndexSizes;
    Indices mIndices;
    Storage &mArray;
};

template<typename T, std::size_t... T_dimensions>
class ArrayIterator<T, StorageOrder::columnMajor, StorageOrder::rowMajor, T_dimensions...>
{
public:
    using Storage = std::array<T, product(T_dimensions...)>;
    using IndexSizes = std::array<std::size_t, sizeof... (T_dimensions)>;
    using Indices = std::array<std::size_t, sizeof... (T_dimensions)>;

    ArrayIterator(Storage& array, const Indices& indices) :
        mIndexSizes({ T_dimensions... }),
        mIndices(indices),
        mArray(array)
    {
        if (mIndices == mIndexSizes)
        {
            for (std::size_t i = 1; i < mIndices.size(); ++i)
            {
                mIndices[i] = 0;
            }
        }
    }

    constexpr bool operator==(const ArrayIterator& other) const
    {
        return &mArray == &other.mArray && mIndices == other.mIndices;
    }

    constexpr bool operator!=(const ArrayIterator& other) const
    {
        return !operator==(other);
    }

    T& operator*()
    {
        return mArray[ArrayIndexer<StorageOrder::columnMajor, T_dimensions...>::computeIndex(mIndices)];
    }

    ArrayIterator& operator++()
    {
        advance(sizeof... (T_dimensions) - 1);
        return *this;
    }

private:
    void advance(std::size_t incrementIndex)
    {
        if (++mIndices[incrementIndex] == mIndexSizes[incrementIndex])
        {
            if (incrementIndex > 0)
            {
                mIndices[incrementIndex] = 0;
                advance(incrementIndex - 1);
            }
        }
    }

private:
    IndexSizes mIndexSizes;
    Indices mIndices;
    Storage &mArray;
};

template<typename T, StorageOrder T_storageOrder, std::size_t... T_dimensions>
class Array
{
public:
    static constexpr std::size_t StorageSize = product(T_dimensions...);
    using Storage = std::array<T, StorageSize>;

    using iterator = typename Storage::iterator;
    using iterator_row = ArrayIterator<T, T_storageOrder, StorageOrder::rowMajor, T_dimensions...>;
    using iterator_col = ArrayIterator<T, T_storageOrder, StorageOrder::columnMajor, T_dimensions...>;

    constexpr bool empty() const
    {
        return mArray.empty();
    }

    constexpr std::size_t size() const
    {
        return mArray.size();
    }

    iterator begin()
    {
        return mArray.begin();
    }

    iterator end()
    {
        return mArray.end();
    }

    iterator_row beginRowMajor()
    {
        return iterator_row(mArray, { 0, 0 });
    }

    iterator_row endRowMajor()
    {
        return iterator_row(mArray, { T_dimensions... });
    }

    iterator_col beginColMajor()
    {
        return iterator_col(mArray, { 0, 0 });
    }

    iterator_col endColMajor()
    {
        return iterator_col(mArray, { T_dimensions... });
    }

    T& operator[](std::size_t index)
    {
        return mArray[index];
    }

    const T& operator[](std::size_t index) const
    {
        return mArray[index];
    }

private:
    Storage mArray;
};

template<typename T, StorageOrder S, std::size_t... D>
std::ostream& operator<<(std::ostream& stream, Array<T, S, D...>& array)
{
    stream << "{";
    if (!array.empty())
    {
        std::size_t index = 0;
        stream << " " << array[index];
        while (++index < array.size())
        {
            stream << ", " << array[index];
        }
    }
    stream << " }";

    return stream;
}

template<typename T, StorageOrder S, std::size_t... D>
void printRowMajor(Array<T, S, D...>& array)
{
    std::cout << "{";
    auto itr = array.beginRowMajor();
    if (itr != array.endRowMajor())
    {
        std::cout << " " << *itr;
        while (++itr != array.endRowMajor())
        {
            std::cout << ", " << *itr;
        }
    }
    std::cout << " }";
}

int main()
{
    {
        Array<int, StorageOrder::rowMajor, 2, 3> rowMajorArray;
        int i = -1;
        // Store the data directly to the array
        for (auto& v : rowMajorArray)
        {
            v = ++i;
        }

        Array<int, StorageOrder::columnMajor, 2, 3> colMajorArray;
        i = -1;
        // Store the data directly to the array
        for (auto& v : colMajorArray)
        {
            v = ++i;
        }

        std::cout << "storage order: " << rowMajorArray << "\n";
        std::cout << "storage order: " << colMajorArray << "\n";
        std::cout << "r major order: "; printRowMajor(rowMajorArray); std::cout << "\n";
        std::cout << "r major order: "; printRowMajor(colMajorArray); std::cout << "\n";
    }

    {
        Array<int, StorageOrder::rowMajor, 2, 2, 2> rowMajorArray;
        int i = -1;
        // Store the data directly to the array
        for (auto& v : rowMajorArray)
        {
            v = ++i;
        }

        Array<int, StorageOrder::columnMajor, 2, 2, 2> colMajorArray;
        i = -1;
        // Store the data directly to the array
        for (auto& v : colMajorArray)
        {
            v = ++i;
        }

        std::cout << "storage order: " << rowMajorArray << "\n";
        std::cout << "storage order: " << colMajorArray << "\n";
        std::cout << "r major order: "; printRowMajor(rowMajorArray); std::cout << "\n";
        std::cout << "r major order: "; printRowMajor(colMajorArray); std::cout << "\n";
    }

    {
        Array<int, StorageOrder::rowMajor, 2, 2, 2, 2> rowMajorArray;
        int i = -1;
        // Store the data directly to the array
        for (auto& v : rowMajorArray)
        {
            v = ++i;
        }

        Array<int, StorageOrder::columnMajor, 2, 2, 2, 2> colMajorArray;
        i = -1;
        // Store the data directly to the array
        for (auto& v : colMajorArray)
        {
            v = ++i;
        }

        std::cout << "storage order: " << rowMajorArray << "\n";
        std::cout << "storage order: " << colMajorArray << "\n";
        std::cout << "r major order: "; printRowMajor(rowMajorArray); std::cout << "\n";
        std::cout << "r major order: "; printRowMajor(colMajorArray); std::cout << "\n";
    }

    {
        Array<int, StorageOrder::rowMajor, 2, 3> rowArray;
        Array<int, StorageOrder::columnMajor, 2, 3> colArray;

        rowArray[0] = 0; rowArray[1] = 1; rowArray[2] = 2;
        rowArray[3] = 3; rowArray[4] = 4; rowArray[5] = 5;

        colArray[0] = 0; colArray[2] = 1; colArray[4] = 2;
        colArray[1] = 3; colArray[3] = 4; colArray[5] = 5;

        std::cout
            << "\nTest case 1\n"
            << "-----------\n"
            << "Both arrays represent the same 2x3 matrix:\n"
            << "  0 1 2\n"
            << "  3 4 5\n";

        std::cout << "row major iteration over rowArray:\n  ";
        for (auto itr = rowArray.beginRowMajor(); itr != rowArray.endRowMajor(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)\n";

        std::cout << "row major iteration over colArray:\n  ";
        for (auto itr = colArray.beginRowMajor(); itr != colArray.endRowMajor(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)\n";

        std::cout << "internal 1-D data iteration over rowArray:\n  ";
        for (auto itr = rowArray.begin(); itr != rowArray.end(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)" << std::endl;

        std::cout << "internal 1-D data iteration over colArray:\n  ";
        for (auto itr = colArray.begin(); itr != colArray.end(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 3 1 4 2 5 (expected)" << std::endl;

        std::cout << "col major iteration over rowArray:\n  ";
        for (auto itr = rowArray.beginColMajor(); itr != rowArray.endColMajor(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 3 1 4 2 5 (expected)\n";

        std::cout << "col major iteration over colArray:\n  ";
        for (auto itr = colArray.beginColMajor(); itr != colArray.endColMajor(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 3 1 4 2 5 (expected)\n";
    }

    {
        Array<int, StorageOrder::rowMajor, 2, 3> rowArray;
        Array<int, StorageOrder::columnMajor, 2, 3> colArray;

        rowArray[0] = 0; rowArray[1] = 1; rowArray[2] = 2;
        rowArray[3] = 3; rowArray[4] = 4; rowArray[5] = 5;

        colArray[0] = 0; colArray[2] = 2; colArray[4] = 4;
        colArray[1] = 1; colArray[3] = 3; colArray[5] = 5;

        std::cout
            << "\nTest case 2\n"
            << "-----------\n"
            << "Both arrays share the same internal 1-D representation:\n"
            << "  0 1 2 3 4 5\n";

        std::cout << "row major iteration over rowArray:\n  ";
        for (auto itr = rowArray.beginRowMajor(); itr != rowArray.endRowMajor(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)\n";

        std::cout << "row major iteration over colArray:\n  ";
        for (auto itr = colArray.beginRowMajor(); itr != colArray.endRowMajor(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 2 4 1 3 5 (expected)\n";

        std::cout << "internal 1-D data iteration over rowArray:\n  ";
        for (auto itr = rowArray.begin(); itr != rowArray.end(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)" << std::endl;

        std::cout << "internal 1-D data iteration over colArray:\n  ";
        for (auto itr = colArray.begin(); itr != colArray.end(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)" << std::endl;

        std::cout << "col major iteration over rowArray:\n  ";
        for (auto itr = rowArray.beginColMajor(); itr != rowArray.endColMajor(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 3 1 4 2 5 (expected)\n";

        std::cout << "col major iteration over colArray:\n  ";
        for (auto itr = colArray.beginColMajor(); itr != colArray.endColMajor(); ++itr)
        {
            std::cout << *itr << " ";
        }
        std::cout << "\n  0 1 2 3 4 5 (expected)\n";
    }

    return 0;
}

答案 1 :(得分:0)

您的代码存在的主要问题是,在operator++ ElementWiseIterator _currentIndices {} {}} {} {{}} {{}}事实上,它甚至不需要具有两个不同的storageIndex函数,因为它们的唯一目的应该是从N维索引转换为1维索引。

鉴于此,我实施了一个小的纠正措施;它并不是非常漂亮,但它有效,你可以在以后感觉到它的美化。

ElementWiseIterator & operator++()
{
    std::size_t i = _shape.size();
    // The idea here is that we keep exactly the same code to increase the index,
    // since that's working fine.
    // However, in column Major order the difference is that we treat the
    // column index as the first index we want to try to increment. Thus,
    // we simply swap it with the row index here, and at the end of the procedure.
    if ( T_storageOrder == StorageOrder::columnMajor ) {
        std::swap(_shape[T_dimensionality-1], _shape[T_dimensionality-2]);
        std::swap(_currentIndices[T_dimensionality-1], _currentIndices[T_dimensionality-2]);
    }
    // For each dimension
    while (i-- > 0) {
        // Find the lowest index one can increase
        // Indeces are stored from top to bottom
        if (_currentIndices[i] < _shape[i] - 1) {
            ++_currentIndices[i];
            break;
        }
    }
    // We want to know if we reached the end of the container.
    // I don't think there's an easy way to directly set the final
    // index from here, given that another swap still needs to happen.
    // So we save this information and use it later.
    bool endOfContainer = ( i == -1 );

    // Then we reset all the others, since they could not be increased.
    for (++i; i < _currentIndices.size(); ++i) {
        _currentIndices[i] = 0;
    }

    if ( T_storageOrder == StorageOrder::columnMajor ) {
        std::swap(_shape[T_dimensionality-1], _shape[T_dimensionality-2]);
        std::swap(_currentIndices[T_dimensionality-1], _currentIndices[T_dimensionality-2]);
    }
    // We manually set the last element
    if ( endOfContainer )
        _currentIndices[0] = _shape[0];

    setCurrentElement();
    return *this;
}

// .....

void setCurrentElement()
{
    // This storageIndex is the row major one; the other you can delete!
    std::size_t index = storageIndex(
        _currentIndices,
        _shape
    );
    _currentElement = &(*_data)[index];
}

您可以在此处试用此代码:http://ideone.com/7Y7LOa

编辑:关于元素的独立于存储顺序的比较,正如我所说,你也可以使用迭代器来写入矩阵。此代码显示了如何编写和比较两个矩阵中的元素,一个使用行主顺序,另一个使用主列。您可以看到内部存储最终不同,但迭代器仍允许您直接比较元素。

http://ideone.com/fzu7uE

答案 2 :(得分:0)

我找到了一个非常简单的问题答案,但我有一种直觉,认为它不是那么干净,即使我没有对它进行分析,它也增加了一些开销。

它包括从

中的问题更新ElementWiseIterator::operator==()方法
friend bool operator==(const ElementWiseIterator &iterator1, const ElementWiseIterator &iterator2)
{ return iterator1._currentElement == iterator2._currentElement; }

friend bool operator==(const ElementWiseIterator &iterator1, const ElementWiseIterator &iterator2)
{ return iterator1._currentIndices == iterator2._currentIndices; }

因此基本上比较迭代器的当前索引而不是当前元素的地址。

答案 3 :(得分:-1)

您可以根据存储顺序实现迭代器完全不同。

struct ElementWiseIterator<StorageOrder so>;

template<>
struct ElementWiseIterator<StorageOrder::rowMajor> {
    [...]
};

template<>
struct ElementWiseIterator<StorageOrder::columnMajor> {
    [...]
};

template <StorageOrder so> 
class Array {
    ElementWiseIterator<so> elementWiseBegin() { 
        return ElementWiseIterator<so>(this);
    }
};