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允许
编辑以回应澄清
\n\nCOUNT您对每次取消引用取迭代器模的想法是一个很好的想法。请参阅下面的反向迭代器/可迭代组合。我用 clang with 编译后检查了程序集输出-O3。编译器展开循环。输出是2 1 0 5 4 3. 您可以实现前向迭代器,或将方向作为参数。您还可以将其作为枚举类型的模板。
不幸的是,从使用语法的角度来看,我认为这不会给你带来太多的do-while循环,如另一个答案 \xe2\x80\x93 所示,至少在 C++11 之前不会。它确实隐藏了各种索引变量,并帮助您避免它们的错误,但它更加冗长。
#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}\nRun 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;\nRun Code Online (Sandbox Code Playgroud)\n\n您可能(ab?)使用宏在 C++98 中获得类似的东西。
\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}\nRun Code Online (Sandbox Code Playgroud)\n\n输出:
\n\nSE S SW NW N NE\nRun Code Online (Sandbox Code Playgroud)\n\n(这些值是int- 大小的枚举,但转换为字符串仅用于输出std::cout)。
这显示了整个集合的迭代,具有任意起点。您可以使其向前或向后移动,并在任何枚举上对其进行模板化。
\n\n我认为在你的问题中使用模是一个好主意。这段代码只是为您提供一些有关枚举中常量数量的反射信息,并将它们放入数组中。
\n\n这可能不适合的原因是,由于您没有使用 C++11,因此该数组Direction::_values()将在程序启动时初始化。我认为循环展开仍然可能发生,但编译器将无法对数组的内容执行任何操作。该数组仍将被静态分配,但元素在编译期间将不可用。
如果您稍后可以选择使用 C++11,则该数组将与静态初始化的数组基本相同int[6](实际上,Direction[6],其中Direction是文字struct类型)。
int(实际上,我想我可以公开s 数组而不是s数组Direction,即使在 C++98 中,它也会被静态初始化)。