迭代方向枚举

Fla*_*ire 5 c++ enums iterator loops

我有一个带有6个方向条目(N,NE,SE,S,SW,NW)的方向枚举,它们基本上是图形中节点的边缘.我经常需要迭代所有邻居,这些邻居当前通过仅使用int从0-> 5迭代完成.

有时候还需要从例如2-> 1的环绕中进行迭代,其中当前通过从2-> 2 + 5迭代并在使用时将其调整为6来完成.

有什么我可以做的更安全/更容易使用而不会失去性能?具有固定整数范围的for循环可以展开,内联等.基于矢量的方法不能(在枚举内使用静态const向量)

我已经在以下方案中使用枚举:

struct Direction{
    enum Type{
        N, NE, ...
    }
    unsigned COUNT = ...;
    Type t_;
    operator Type(){return t_;}
    Direction(Type t):t_(t){assert(N<=t<COUNT);}
    explicit Direction(unsigned t):t_(t%COUNT){}
    static Direction fromUInt(unsigned t){return Direction(Type(t));}
}
Run Code Online (Sandbox Code Playgroud)

所以我想要的是拥有允许在整个集合上有效迭代的迭代器,并允许这个任意起始点,在这种情况下迭代器会包装.

怎么会写这个?我无法想象任何事情,因为我会有例如start = end的整个循环,这将是无效的.或者我只有start = givenStartType,end = start + COUNT并在每个迭代器deref上做一个模数?

不幸的是,没有C++ 11允许

ant*_*ron 2

编辑以回应澄清

\n\n

COUNT您对每次取消引用取迭代器模的想法是一个很好的想法。请参阅下面的反向迭代器/可迭代组合。我用 clang with 编译后检查了程序集输出-O3。编译器展开循环。输出是2 1 0 5 4 3. 您可以实现前向迭代器,或将方向作为参数。您还可以将其作为枚举类型的模板。

\n\n

不幸的是,从使用语法的角度来看,我认为这不会给你带来太多的do-while循环,如另一个答案 \xe2\x80\x93 所示,至少在 C++11 之前不会。它确实隐藏了各种索引变量,并帮助您避免它们的错误,但它更加冗长。

\n\n
#include <iostream>\n\nstruct Direction {\n    enum Type {N, NE, SE, S, SW, NW};\n\n    static const unsigned COUNT = 6;\n\n    Type t_;\n\n    operator Type() { return t_; }\n    Direction(Type t) : t_(t) { }\n    explicit Direction(unsigned t) : t_(Type(t % COUNT)) { }\n};\n\nstruct ReverseIterable {\n    const unsigned      start_;\n\n    struct iterator {\n        unsigned        offset_;\n\n        explicit iterator(unsigned offset) : offset_(offset) { }\n\n        Direction operator *() { return Direction(offset_); }\n        iterator& operator++() { --offset_; return *this; }\n\n        bool operator ==(const iterator &other)\n            { return offset_ == other.offset_; }\n        bool operator !=(const iterator &other) { return !(*this == other); }\n    };\n\n    explicit ReverseIterable(Direction start) : start_(start) { }\n\n    iterator begin() { return iterator(start_ + Direction::COUNT); }\n    iterator end() { return iterator(start_); }\n};\n\nint main()\n{\n    ReverseIterable     range = ReverseIterable(Direction::SE);\n\n    for (ReverseIterable::iterator iterator = range.begin();\n         iterator != range.end(); ++iterator) {\n\n        std::cout << (int)*iterator << " ";\n    }\n    std::cout << std::endl;\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

在 C++11 中,循环可以是:

\n\n
    for (Direction direction : ReverseIterable(Direction::SE))\n        std::cout << (int)direction << " ";\n    std::cout << std::endl;\n
Run Code Online (Sandbox Code Playgroud)\n\n

您可能(ab?)使用宏在 C++98 中获得类似的东西。

\n\n

我暂时保留了下面的原始答案,因为如果枚举定义可以更改,它可以简化可维护性,并且因为它允许稀疏范围。可以在其之上实现一个非常相似的迭代器。

\n\n
\n\n

最初的答案侧重于安全

\n\n

对于您的目的来说,这可能完全是矫枉过正,并且由于我将在下面进一步描述的原因,它可能不适合。但是,您可以使用这个库(免责声明:我是作者): https: //github.com/aantron/better-enums编写如下代码:

\n\n
#include <iostream>\n#include <enum.h>\n\nENUM(Direction, int, N, NE, SE, S, SW, NW)\n\nint main()\n{\n    size_t  iterations = Direction::_size();\n    size_t  index = 2;\n\n    for (size_t count = 0; count < iterations; ++count) {\n        std::cout << Direction::_values()[index] << " ";\n        index = (index + 1) % Direction::_size();\n    }\n    std::cout << std::endl;\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

输出:

\n\n
SE S SW NW N NE\n
Run Code Online (Sandbox Code Playgroud)\n\n

(这些值是int- 大小的枚举,但转换为字符串仅用于输出std::cout)。

\n\n

这显示了整个集合的迭代,具有任意起点。您可以使其向前或向后移动,并在任何枚举上对其进行模板化。

\n\n

我认为在你的问题中使用模是一个好主意。这段代码只是为您提供一些有关枚举中常量数量的反射信息,并将它们放入数组中。

\n\n

这可能不适合的原因是,由于您没有使用 C++11,因此该数组Direction::_values()将在程序启动时初始化。我认为循环展开仍然可能发生,但编译器将无法对数组的内容执行任何操作。该数组仍将被静态分配,但元素在编译期间将不可用。

\n\n

如果您稍后可以选择使用 C++11,则该数组将与静态初始化的数组基本相同int[6](实际上,Direction[6],其中Direction是文字struct类型)。

\n\n

int(实际上,我想我可以公开s 数组而不是s数组Direction,即使在 C++98 中,它也会被静态初始化)。

\n