C++ 迭代方向枚举
我有一个方向枚举,有6个方向条目(N、NE、SE、S、SW、NW),它们基本上是图中节点的边。我经常需要迭代所有的邻居,这是通过仅使用int从0->5进行迭代来完成的 有时还需要从2->1开始迭代,当前是通过从2->2+5开始迭代并使用mod 6完成的 我能做些什么使它更安全/更易于使用而不损失性能?具有固定整数范围的for循环可以展开、内联等。基于向量的方法不能(在枚举中使用静态常量向量) 我已经在类似以下的方案中使用了枚举:C++ 迭代方向枚举,c++,loops,enums,iterator,C++,Loops,Enums,Iterator,我有一个方向枚举,有6个方向条目(N、NE、SE、S、SW、NW),它们基本上是图中节点的边。我经常需要迭代所有的邻居,这是通过仅使用int从0->5进行迭代来完成的 有时还需要从2->1开始迭代,当前是通过从2->2+5开始迭代并使用mod 6完成的 我能做些什么使它更安全/更易于使用而不损失性能?具有固定整数范围的for循环可以展开、内联等。基于向量的方法不能(在枚举中使用静态常量向量) 我已经在类似以下的方案中使用了枚举: struct Direction{ enum Type{
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));}
}
结构方向{
枚举类型{
N、 嗯。。。
}
无符号计数=。。。;
t型;
运算符类型()
方向(类型t):t_uT{assert(N编辑以响应澄清
您在每次解引用时使用迭代器模计数的想法很好。请参阅下面的反向迭代器/iterable组合。在使用-O3
进行编译后,我检查了程序集输出。编译器展开了循环。输出为2 1 0 5 4 3
。您可以实现正向迭代器,或者方向是一个参数。您也可以将其设置为枚举类型上的模板
不幸的是,从使用语法的角度来看,我认为这并不能像另一个答案所示那样在do
-的循环中为您带来太多好处,至少在C++11之前是这样。它确实隐藏了各种索引变量,并帮助您避免错误,但它要详细得多
#include <iostream>
struct Direction {
enum Type {N, NE, SE, S, SW, NW};
static const unsigned COUNT = 6;
Type t_;
operator Type() { return t_; }
Direction(Type t) : t_(t) { }
explicit Direction(unsigned t) : t_(Type(t % COUNT)) { }
};
struct ReverseIterable {
const unsigned start_;
struct iterator {
unsigned offset_;
explicit iterator(unsigned offset) : offset_(offset) { }
Direction operator *() { return Direction(offset_); }
iterator& operator++() { --offset_; return *this; }
bool operator ==(const iterator &other)
{ return offset_ == other.offset_; }
bool operator !=(const iterator &other) { return !(*this == other); }
};
explicit ReverseIterable(Direction start) : start_(start) { }
iterator begin() { return iterator(start_ + Direction::COUNT); }
iterator end() { return iterator(start_); }
};
int main()
{
ReverseIterable range = ReverseIterable(Direction::SE);
for (ReverseIterable::iterator iterator = range.begin();
iterator != range.end(); ++iterator) {
std::cout << (int)*iterator << " ";
}
std::cout << std::endl;
return 0;
}
(值是int
大小的枚举,但被转换为字符串,仅输出到std::cout
)
这显示了在整个集合上的迭代,具有任意的起始点。您可以使其向前或向后,并在任何枚举上对其进行模板化
我认为在你的问题中使用模是一个好主意。这段代码只是提供了一些有关枚举中常量数量的反射信息,并将它们放入数组中
这可能不适合的原因是,因为您没有使用C++11,所以数组Direction::_values()
将在程序启动时初始化。我认为循环展开仍然可以发生,但编译器将无法对数组的内容执行任何操作。数组仍将静态分配,但元素在编译期间不可用
如果您以后可以选择使用C++11,该数组将基本上与静态初始化的int[6]
(实际上,Direction[6]
,其中Direction
是一个文本struct
类型)相同
(实际上,我想我可以公开一个int
s数组,而不是Direction
s数组,即使在C++98中也可以静态初始化)。如果您想避免使用自定义库,我通常看到的方法是这样的:
#include <iostream>
#include <enum.h>
ENUM(Direction, int, N, NE, SE, S, SW, NW)
int main()
{
size_t iterations = Direction::_size();
size_t index = 2;
for (size_t count = 0; count < iterations; ++count) {
std::cout << Direction::_values()[index] << " ";
index = (index + 1) % Direction::_size();
}
std::cout << std::endl;
return 0;
}
enum Direction
{
SE,
S,
SW,
NW,
N,
NE,
DIRECTION_FIRST = SE,
DIRECTION_LAST = NE,
}
Direction current = start;
do
{
// Do stuff with current...
current = (current + 1) % DIRECTION_LAST;
} while(current != start);
然后,您可以使用DIRECTION\u FIRST
和DIRECTION\u LAST
在该范围内正确迭代。如果有人在不更新迭代端点的情况下向枚举中添加值,则仍有出错的空间,但将其集中在枚举中会降低这种可能性
现在,如果您假设一个任意的开始方向start
,您可以这样迭代:
#include <iostream>
#include <enum.h>
ENUM(Direction, int, N, NE, SE, S, SW, NW)
int main()
{
size_t iterations = Direction::_size();
size_t index = 2;
for (size_t count = 0; count < iterations; ++count) {
std::cout << Direction::_values()[index] << " ";
index = (index + 1) % Direction::_size();
}
std::cout << std::endl;
return 0;
}
enum Direction
{
SE,
S,
SW,
NW,
N,
NE,
DIRECTION_FIRST = SE,
DIRECTION_LAST = NE,
}
Direction current = start;
do
{
// Do stuff with current...
current = (current + 1) % DIRECTION_LAST;
} while(current != start);
(如果枚举不是从0开始的,逻辑可能会更复杂一些。这可能是唯一需要首先使用DIRECTION\u
)我已经有两个构造函数使用整数参数将其转换为DIRECTION。所以我看不到任何好处。特别是我需要更多的变量(至少计数和索引)看编辑后的问题。我明白了。我想我(和其他回答者)一直在关注你问题的安全部分,从某种意义上说(我们假设)对更改枚举定义的抵制。我将编辑该问题以绘制一个迭代器。实际上,您的问题似乎已经对迭代器有了正确的想法–对每个解引用进行模运算。正如我所提到的,我认为这是正确的做法。重点是将上述代码移动到函数中或将其分解为iterators.我仍然会给出一个例子,但是是“在每个解引用上做模”足够回答吗?既然你已经有了COUNT
并且似乎假设了一个密集的范围,那么还有什么关于安全性的问题呢?看起来很好。不可逆iterable应该足够了,但反向是一个奖励。我不允许从int构造方向以避免扩展的模,而是在迭代器中这样做。一个避免所有负数的ideo是使用start+COUNT作为开始。您可能可以执行以下操作:for(ReverseIterable::iterator iterator=Direction::Iterable(Direction::NE)。begin();iterator!=Direction::Iterable(Direction::NE)。end();++iterator)或者用一个宏来模拟C++11循环。很好的建议。我在答案中根据它们修改了代码,除了宏——我刚才提到了这一点,因为推荐它会引起争议:)此外,如果你认为反射库作为答案的一部分是个坏主意,那么,如果你编辑你的问题,说常数范围被假定为密集/连续的,我将剪掉该文本以保持答案简洁。这只适用于其他可能有类似问题的用户。问题中没有工作代码。c发布的ode仅仅是问题所指的枚举的结构。在代码下发布的只是开始的想法,而不是更多