gpt4 book ai didi

c++ - 迭代方向枚举

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:43:38 26 4
gpt4 key购买 nike

我有一个方向枚举,其中包含 6 个方向条目(N、NE、SE、S、SW、NW),它们基本上是图中节点的边。我经常需要遍历所有邻居,目前仅使用 int 从 0->5 进行迭代。

有时还需要从例如迭代2->1 环绕,目前通过从 2->2+5 迭代并在使用时取模 6 来完成。

有什么我可以做的,可以在不损失性能的情况下使它更安全/更容易使用吗?具有固定整数范围的 for 循环可以展开、内联等。基于 vector 的方法不能(在枚举中使用静态常量 vector )

我已经在这样的方案中有了枚举:

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));}
}

所以我想要的是拥有允许在整个集合上高效迭代的迭代器,并且还允许对任意起点进行迭代,在这种情况下迭代器环绕。

这个怎么写?我想不出任何东西,例如整个循环的 start=end 是无效的。或者我应该只使用 start=givenStartType、end=start+COUNT 并对每个迭代器取消引用取模吗?

不幸的是不允许使用 C++11

最佳答案

编辑以回应澄清

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

不幸的是,从使用语法的角度来看,我不认为这比另一个所示的 do-while 循环更能给你带来好处答案——至少在 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;
}

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

    for (Direction direction : ReverseIterable(Direction::SE))
std::cout << (int)direction << " ";
std::cout << std::endl;

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

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


关注安全的原始答案

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

#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;
}

输出:

SE S SW NW N NE

(值是 int 大小的枚举,但被转换为字符串以仅输出到 std::cout)。

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

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

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

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

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

关于c++ - 迭代方向枚举,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31819727/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com