自定义迭代器:如果差异

时间:2018-04-16 09:51:20

标签: c++ iterator c++17

我编写了一个类PointerIterator,它为数组提供了一个随机访问迭代器。我知道指针本身是有效的迭代器,但我的实现允许指定单元。此单元指定每个增量或减量所采取的步骤。其背后的原因是在 n x m 矩阵中的一行上启用迭代,其中矩阵以行主要顺序存储为单个数组。

但是,因为 unit 在编译时是未知的,所以必须在运行时将其指定为ctor参数。这导致了两个相同类型的迭代器可以产生不同行为的问题(如果 unit 彼此不同)。

实施例

m 是由单个数组表示的4x4矩阵(按行主顺序排列)。

auto rowSize = 4;
auto colSize = 4;
auto m = new int[rowSize * colSize];

// m is somehow filled with increasing numbers, like:

  0   1   2   3    
  4   5   6   7
  8   9  10  11
 12  13  14  15

要遍历 A 的第一行,会创建两个PointerIterator(标记范围的开头和结尾)

auto begin = makePointerIterator(m, 1);
auto end = begin + rowSize;

其中makePointerIterator(pointer, unit)使用步骤 unit 为指针指针创建一个PointerIterator。

使用上面的beginend,我们可以使用for循环打印第一行,例如:

for (auto it = begin; it != end; ++it)
    cout << std::setw(3) << *it << " ";

产生

  0   1   2   3

要迭代第一列,我们必须将beginend更改为

auto begin = makePointerIterator(m, rowSize);
auto end = begin + colSize;

每个增量(即operator++)会使指针前进rowSize,指向列中的下一个元素。

打印列现在可以通过上面的循环完成,现在产生:

  0   4   8  12

问题

为了制作一个valid随机访问迭代器,我必须提供(其中包括)

difference_type operator-(PointerIterator other) const;
bool operator==(PointerIterator other) const;
bool operator!=(PointerIterator other) const;

假设我有两个PointerIterator ab a.unit == 1b.unit == 2,这意味着a将指针增加1并b在每次递增(或递减)时将指针增加(或减少)。

如何在保持迭代器标准符合的同时实现上述3个函数?

我想到的可能的解决方案是:

  1. 抢先检查是否a.unit == b.unit,如果没有,则抛出异常。
  2. 始终使用a.unit(位于运算符的左侧),这会导致a == b可能不等于b == a
  3. 修改

    我将实施放在codereview上以防有人对此感兴趣。

2 个答案:

答案 0 :(得分:3)

相同类型但具有不同单位的迭代器可以采用与相同类型的迭代器相同的方式处理,但指向不同的容器。也就是说,程序员有责任确保单位是相同的,否则它是未定义的行为来混合(比较或减去)迭代器。

答案 1 :(得分:0)

首先,未定义的行为是你的朋友。有“安全”操作抛出或使用代数错误类型来指示不匹配,但==操作可以简单地使结果未定义,比如比较不同容器的迭代器。

其次,考虑这样做:

template<class Unit, class=void>
struct default_unit_value {};

template<class Unit,
  std::enable_if_t< std::is_integral<Unit>{} >
>
struct default_unit_value:std::integral_constant<Unit, 1> {};

template<class T, class Unit=std::ptrdiff_t>
struct PointerIterator {
  PointerIterator(T* p = nullptr, Unit u = default_unit_value<Unit>::value):
    ptr(p), unit(u)
  {}
private:
  T* ptr;
  Unit unit; // actually use a compressed pair, in case Unit is stateless
};

这允许使用编译时常量单位大小,并且在编译时检查这些情况下的不匹配错误。

auto begin = makePointerIterator<1>(m);
auto end = begin + rowSize;

constexpr rowSize = 4;
auto begin = makePointerIterator<rowSize>(m);
auto end = begin + colSize;

同时,运行时变量步幅仍然可用。

在很多情况下,您确实知道编译时的元素步幅距离。明确地告诉编译器将生成更好的代码。